From 1df9c49c46ce13b5be438f51960f40fa67f66ecb Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 8 Aug 2013 00:27:49 -0700 Subject: [PATCH] Ellipsis added, but doesn't play nice with onRender... yet. --- UI/AddSeries/SearchResultView.js | 10 +- UI/AddSeries/addSeries.less | 4 +- UI/JsLibraries/jquery.dotdotdot.js | 632 +++++++++++++++++++++++++++++ UI/app.js | 8 + 4 files changed, 650 insertions(+), 4 deletions(-) create mode 100644 UI/JsLibraries/jquery.dotdotdot.js diff --git a/UI/AddSeries/SearchResultView.js b/UI/AddSeries/SearchResultView.js index de88520cf..5fb6101f0 100644 --- a/UI/AddSeries/SearchResultView.js +++ b/UI/AddSeries/SearchResultView.js @@ -3,12 +3,14 @@ define( [ 'app', 'marionette', + 'Quality/QualityProfileCollection', 'AddSeries/RootFolders/Collection', 'AddSeries/RootFolders/Layout', 'Series/SeriesCollection', 'Config', - 'Shared/Messenger' + 'Shared/Messenger', + 'jquery.dotdotdot' ], function (App, Marionette, QualityProfiles, RootFolders, RootFolderLayout, SeriesCollection, Config, Messenger) { return Marionette.ItemView.extend({ @@ -54,6 +56,12 @@ define( if (RootFolders.get(defaultRoot)) { this.ui.rootFolder.val(defaultRoot); } + + //TODO: make this work via onRender, FM? + //works with onShow, but stops working after the first render + this.ui.overview.dotdotdot({ + height: 120 + }); }, serializeData: function () { diff --git a/UI/AddSeries/addSeries.less b/UI/AddSeries/addSeries.less index 638f4f130..2e9cd7556 100644 --- a/UI/AddSeries/addSeries.less +++ b/UI/AddSeries/addSeries.less @@ -58,10 +58,8 @@ .search-item { .new-series-overview { - text-overflow : ellipsis; - white-space : pre-line; overflow : hidden; - height : 125px; + height : 120px; } .new-series-poster { diff --git a/UI/JsLibraries/jquery.dotdotdot.js b/UI/JsLibraries/jquery.dotdotdot.js new file mode 100644 index 000000000..c7f58d039 --- /dev/null +++ b/UI/JsLibraries/jquery.dotdotdot.js @@ -0,0 +1,632 @@ +/* + * jQuery dotdotdot 1.6.1 + * + * Copyright (c) 2013 Fred Heusschen + * www.frebsite.nl + * + * Plugin website: + * dotdotdot.frebsite.nl + * + * Dual licensed under the MIT and GPL licenses. + * http://en.wikipedia.org/wiki/MIT_License + * http://en.wikipedia.org/wiki/GNU_General_Public_License + */ + +(function( $ ) +{ + if ( $.fn.dotdotdot ) + { + return; + } + + $.fn.dotdotdot = function( o ) + { + if ( this.length == 0 ) + { + if ( !o || o.debug !== false ) + { + debug( true, 'No element found for "' + this.selector + '".' ); + } + return this; + } + if ( this.length > 1 ) + { + return this.each( + function() + { + $(this).dotdotdot( o ); + } + ); + } + + + var $dot = this; + + if ( $dot.data( 'dotdotdot' ) ) + { + $dot.trigger( 'destroy.dot' ); + } + + $dot.data( 'dotdotdot-style', $dot.attr( 'style' ) ); + $dot.css( 'word-wrap', 'break-word' ); + if ($dot.css( 'white-space' ) === 'nowrap') + { + $dot.css( 'white-space', 'normal' ); + } + + $dot.bind_events = function() + { + $dot.bind( + 'update.dot', + function( e, c ) + { + e.preventDefault(); + e.stopPropagation(); + + opts.maxHeight = ( typeof opts.height == 'number' ) + ? opts.height + : getTrueInnerHeight( $dot ); + + opts.maxHeight += opts.tolerance; + + if ( typeof c != 'undefined' ) + { + if ( typeof c == 'string' || c instanceof HTMLElement ) + { + c = $('
').append( c ).contents(); + } + if ( c instanceof $ ) + { + orgContent = c; + } + } + + $inr = $dot.wrapInner( '
' ).children(); + $inr.empty() + .append( orgContent.clone( true ) ) + .css({ + 'height' : 'auto', + 'width' : 'auto', + 'border' : 'none', + 'padding' : 0, + 'margin' : 0 + }); + + var after = false, + trunc = false; + + if ( conf.afterElement ) + { + after = conf.afterElement.clone( true ); + conf.afterElement.remove(); + } + if ( test( $inr, opts ) ) + { + if ( opts.wrap == 'children' ) + { + trunc = children( $inr, opts, after ); + } + else + { + trunc = ellipsis( $inr, $dot, $inr, opts, after ); + } + } + $inr.replaceWith( $inr.contents() ); + $inr = null; + + if ( $.isFunction( opts.callback ) ) + { + opts.callback.call( $dot[ 0 ], trunc, orgContent ); + } + + conf.isTruncated = trunc; + return trunc; + } + + ).bind( + 'isTruncated.dot', + function( e, fn ) + { + e.preventDefault(); + e.stopPropagation(); + + if ( typeof fn == 'function' ) + { + fn.call( $dot[ 0 ], conf.isTruncated ); + } + return conf.isTruncated; + } + + ).bind( + 'originalContent.dot', + function( e, fn ) + { + e.preventDefault(); + e.stopPropagation(); + + if ( typeof fn == 'function' ) + { + fn.call( $dot[ 0 ], orgContent ); + } + return orgContent; + } + + ).bind( + 'destroy.dot', + function( e ) + { + e.preventDefault(); + e.stopPropagation(); + + $dot.unwatch() + .unbind_events() + .empty() + .append( orgContent ) + .attr( 'style', $dot.data( 'dotdotdot-style' ) ) + .data( 'dotdotdot', false ); + } + ); + return $dot; + }; // /bind_events + + $dot.unbind_events = function() + { + $dot.unbind('.dot'); + return $dot; + }; // /unbind_events + + $dot.watch = function() + { + $dot.unwatch(); + if ( opts.watch == 'window' ) + { + var $window = $(window), + _wWidth = $window.width(), + _wHeight = $window.height(); + + $window.bind( + 'resize.dot' + conf.dotId, + function() + { + if ( _wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix ) + { + _wWidth = $window.width(); + _wHeight = $window.height(); + + if ( watchInt ) + { + clearInterval( watchInt ); + } + watchInt = setTimeout( + function() + { + $dot.trigger( 'update.dot' ); + }, 10 + ); + } + } + ); + } + else + { + watchOrg = getSizes( $dot ); + watchInt = setInterval( + function() + { + var watchNew = getSizes( $dot ); + if ( watchOrg.width != watchNew.width || + watchOrg.height != watchNew.height ) + { + $dot.trigger( 'update.dot' ); + watchOrg = getSizes( $dot ); + } + }, 100 + ); + } + return $dot; + }; + $dot.unwatch = function() + { + $(window).unbind( 'resize.dot' + conf.dotId ); + if ( watchInt ) + { + clearInterval( watchInt ); + } + return $dot; + }; + + var orgContent = $dot.contents(), + opts = $.extend( true, {}, $.fn.dotdotdot.defaults, o ), + conf = {}, + watchOrg = {}, + watchInt = null, + $inr = null; + + + if ( !( opts.lastCharacter.remove instanceof Array ) ) + { + opts.lastCharacter.remove = $.fn.dotdotdot.defaultArrays.lastCharacter.remove; + } + if ( !( opts.lastCharacter.noEllipsis instanceof Array ) ) + { + opts.lastCharacter.noEllipsis = $.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis; + } + + + conf.afterElement = getElement( opts.after, $dot ); + conf.isTruncated = false; + conf.dotId = dotId++; + + + $dot.data( 'dotdotdot', true ) + .bind_events() + .trigger( 'update.dot' ); + + if ( opts.watch ) + { + $dot.watch(); + } + + return $dot; + }; + + + // public + $.fn.dotdotdot.defaults = { + 'ellipsis' : '... ', + 'wrap' : 'word', + 'fallbackToLetter' : true, + 'lastCharacter' : {}, + 'tolerance' : 0, + 'callback' : null, + 'after' : null, + 'height' : null, + 'watch' : false, + 'windowResizeFix' : true, + 'debug' : false + }; + $.fn.dotdotdot.defaultArrays = { + 'lastCharacter' : { + 'remove' : [ ' ', '\u3000', ',', ';', '.', '!', '?' ], + 'noEllipsis' : [] + } + }; + + + // private + var dotId = 1; + + function children( $elem, o, after ) + { + var $elements = $elem.children(), + isTruncated = false; + + $elem.empty(); + + for ( var a = 0, l = $elements.length; a < l; a++ ) + { + var $e = $elements.eq( a ); + $elem.append( $e ); + if ( after ) + { + $elem.append( after ); + } + if ( test( $elem, o ) ) + { + $e.remove(); + isTruncated = true; + break; + } + else + { + if ( after ) + { + after.detach(); + } + } + } + return isTruncated; + } + function ellipsis( $elem, $d, $i, o, after ) + { + var $elements = $elem.contents(), + isTruncated = false; + + $elem.empty(); + + var notx = 'table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style'; + for ( var a = 0, l = $elements.length; a < l; a++ ) + { + + if ( isTruncated ) + { + break; + } + + var e = $elements[ a ], + $e = $(e); + + if ( typeof e == 'undefined' ) + { + continue; + } + + $elem.append( $e ); + if ( after ) + { + $elem[ ( $elem.is( notx ) ) ? 'after' : 'append' ]( after ); + } + if ( e.nodeType == 3 ) + { + if ( test( $i, o ) ) + { + isTruncated = ellipsisElement( $e, $d, $i, o, after ); + } + } + else + { + isTruncated = ellipsis( $e, $d, $i, o, after ); + } + + if ( !isTruncated ) + { + if ( after ) + { + after.detach(); + } + } + } + return isTruncated; + } + function ellipsisElement( $e, $d, $i, o, after ) + { + var isTruncated = false, + e = $e[ 0 ]; + + if ( typeof e == 'undefined' ) + { + return false; + } + + var txt = getTextContent( e ), + space = ( txt.indexOf(' ') !== -1 ) ? ' ' : '\u3000', + separator = ( o.wrap == 'letter' ) ? '' : space, + textArr = txt.split( separator ), + position = -1, + midPos = -1, + startPos = 0, + endPos = textArr.length - 1; + + while ( startPos <= endPos && !( startPos == 0 && endPos == 0 ) ) + { + var m = Math.floor( ( startPos + endPos ) / 2 ); + if ( m == midPos ) + { + break; + } + midPos = m; + + setTextContent( e, textArr.slice( 0, midPos + 1 ).join( separator ) + o.ellipsis ); + + if ( !test( $i, o ) ) + { + position = midPos; + startPos = midPos; + } + else + { + endPos = midPos; + } + if( endPos == startPos && endPos == 0 && o.fallbackToLetter ) + { + separator = ''; + textArr = textArr[0].split(separator); + position = -1; + midPos = -1; + startPos = 0; + endPos = textArr.length - 1; + } + } + + if ( position != -1 && !( textArr.length == 1 && textArr[ 0 ].length == 0 ) ) + { + txt = addEllipsis( textArr.slice( 0, position + 1 ).join( separator ), o ); + isTruncated = true; + setTextContent( e, txt ); + } + else + { + var $w = $e.parent(); + $e.remove(); + + var afterLength = ( after ) ? after.length : 0 ; + + if ( $w.contents().size() > afterLength ) + { + var $n = $w.contents().eq( -1 - afterLength ); + isTruncated = ellipsisElement( $n, $d, $i, o, after ); + } + else + { + var $p = $w.prev() + var e = $p.contents().eq( -1 )[ 0 ]; + + if ( typeof e != 'undefined' ) + { + var txt = addEllipsis( getTextContent( e ), o ); + setTextContent( e, txt ); + if ( after ) + { + $p.append( after ); + } + $w.remove(); + isTruncated = true; + } + + } + } + + return isTruncated; + } + function test( $i, o ) + { + return $i.innerHeight() > o.maxHeight; + } + function addEllipsis( txt, o ) + { + while( $.inArray( txt.slice( -1 ), o.lastCharacter.remove ) > -1 ) + { + txt = txt.slice( 0, -1 ); + } + if ( $.inArray( txt.slice( -1 ), o.lastCharacter.noEllipsis ) < 0 ) + { + txt += o.ellipsis; + } + return txt; + } + function getSizes( $d ) + { + return { + 'width' : $d.innerWidth(), + 'height': $d.innerHeight() + }; + } + function setTextContent( e, content ) + { + if ( e.innerText ) + { + e.innerText = content; + } + else if ( e.nodeValue ) + { + e.nodeValue = content; + } + else if (e.textContent) + { + e.textContent = content; + } + + } + function getTextContent( e ) + { + if ( e.innerText ) + { + return e.innerText; + } + else if ( e.nodeValue ) + { + return e.nodeValue; + } + else if ( e.textContent ) + { + return e.textContent; + } + else + { + return ""; + } + } + function getElement( e, $i ) + { + if ( typeof e == 'undefined' ) + { + return false; + } + if ( !e ) + { + return false; + } + if ( typeof e == 'string' ) + { + e = $(e, $i); + return ( e.length ) + ? e + : false; + } + if ( typeof e == 'object' ) + { + return ( typeof e.jquery == 'undefined' ) + ? false + : e; + } + return false; + } + function getTrueInnerHeight( $el ) + { + var h = $el.innerHeight(), + a = [ 'paddingTop', 'paddingBottom' ]; + + for ( var z = 0, l = a.length; z < l; z++ ) { + var m = parseInt( $el.css( a[ z ] ), 10 ); + if ( isNaN( m ) ) + { + m = 0; + } + h -= m; + } + return h; + } + function debug( d, m ) + { + if ( !d ) + { + return false; + } + if ( typeof m == 'string' ) + { + m = 'dotdotdot: ' + m; + } + else + { + m = [ 'dotdotdot:', m ]; + } + + if ( typeof window.console != 'undefined' ) + { + if ( typeof window.console.log != 'undefined' ) + { + window.console.log( m ); + } + } + return false; + } + + + // override jQuery.html + var _orgHtml = $.fn.html; + $.fn.html = function( str ) { + if ( typeof str != 'undefined' ) + { + if ( this.data( 'dotdotdot' ) ) + { + if ( typeof str != 'function' ) + { + return this.trigger( 'update', [ str ] ); + } + } + return _orgHtml.call( this, str ); + } + return _orgHtml.call( this ); + }; + + + // override jQuery.text + var _orgText = $.fn.text; + $.fn.text = function( str ) { + if ( typeof str != 'undefined' ) + { + if ( this.data( 'dotdotdot' ) ) + { + var temp = $( '
' ); + temp.text( str ); + str = temp.html(); + temp.remove(); + return this.trigger( 'update', [ str ] ); + } + return _orgText.call( this, str ); + } + return _orgText.call( this ); + }; + + +})( jQuery ); diff --git a/UI/app.js b/UI/app.js index babb84c19..31b77a49a 100644 --- a/UI/app.js +++ b/UI/app.js @@ -22,6 +22,7 @@ require.config({ 'marionette' : 'JsLibraries/backbone.marionette', 'signalR' : 'JsLibraries/jquery.signalR', 'jquery.knob' : 'JsLibraries/jquery.knob', + 'jquery.dotdotdot' : 'JsLibraries/jquery.dotdotdot', 'libs' : 'JsLibraries/' }, @@ -114,6 +115,13 @@ require.config({ ] }, + 'jquery.dotdotdot': { + deps: + [ + '$' + ] + }, + 'backbone.pageable': { deps: [