diff --git a/README.md b/README.md index f30577f2..cde0f863 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,63 @@ -#![preview thumb](https://github.com/rembo10/headphones/raw/master/data/images/headphoneslogo.png)Headphones - -###Support & Discuss - -You are free to join the HP support community on IRC where you can ask questions, hang around and discuss anything related to HP. - -1. Use any IRC client and connect to the Freenode server. -2. Join #headphones - -###Installation and Notes - -[Read our Wiki](../../wiki) on how to install and use HeadPhones properly. - -**Issues** can be reported on the GitHub issue tracker considering these rules: - -1. Analyze your log, you just might find the solution yourself! -2. You read the wiki and searched existing issues, but this is not solving your problem. -3. Post the issue with a clear title, description and the HP log and use [proper markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your text (code/log in code blocks). -4. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it. - -**Feature requests** can be reported on the GitHub issue tracker too: - -1. Search for similar existing 'issues', feature requests can be recognized by the label 'Request'. -2. If a similar Request exists, post a comment (+1, or add a new idea to the existing request), otherwise you can create a new one. - -If you **comply with these rules** you can [post your request/issue](http://github.com/rembo10/headphones/issues). - -**Support** the project by implementing new features, solving support tickets and provide bug fixes. -If you change something in the code always make a PR to the developer branch instead of the master branch. - - -###Screenshots - -Homepage (Artist Overview) - -![preview thumb](http://i.imgur.com/LZO9a.png) - -One of the many settings pages.... - -![preview thumb](http://i.imgur.com/xcWNy.png) - -It might even know you better than you know yourself: - -![preview thumb](http://i.imgur.com/R7J0f.png) - -Import Your Favorite Artists: - -![preview thumb](http://i.imgur.com/6tZoC.png) - -Artist Search Results (also search by album!): - -![preview thumb](http://i.imgur.com/rIV0P.png) - -Artist Page with Bio & Album Overview: - -![preview thumb](http://i.imgur.com/SSil1.png) - -Album Page with track overview: - -![preview thumb](http://i.imgur.com/kcjES.png) - - -This is free software under the GPL v3 open source license - so feel free to do with it what you wish. +#![preview thumb](https://github.com/rembo10/headphones/raw/master/data/images/headphoneslogo.png)Headphones + +###Support & Discuss + +You are free to join the HP support community on IRC where you can ask questions, hang around and discuss anything related to HP. + +1. Use any IRC client and connect to the Freenode server. +2. Join #headphones + +###Installation and Notes + +[Read our Wiki](../../wiki) on how to install and use HeadPhones properly. + +**Issues** can be reported on the GitHub issue tracker considering these rules: + +1. Analyze your log, you just might find the solution yourself! +2. You read the wiki and searched existing issues, but this is not solving your problem. +3. Post the issue with a clear title, description and the HP log and use [proper markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your text (code/log in code blocks). +4. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it. + +**Feature requests** can be reported on the GitHub issue tracker too: + +1. Search for similar existing 'issues', feature requests can be recognized by the label 'Request'. +2. If a similar Request exists, post a comment (+1, or add a new idea to the existing request), otherwise you can create a new one. + +If you **comply with these rules** you can [post your request/issue](http://github.com/rembo10/headphones/issues). + +**Support** the project by implementing new features, solving support tickets and provide bug fixes. +If you change something in the code always make a PR to the developer branch instead of the master branch. + + +###Screenshots + +Homepage (Artist Overview) + +![preview thumb](http://i.imgur.com/LZO9a.png) + +One of the many settings pages.... + +![preview thumb](http://i.imgur.com/xcWNy.png) + +It might even know you better than you know yourself: + +![preview thumb](http://i.imgur.com/R7J0f.png) + +Import Your Favorite Artists: + +![preview thumb](http://i.imgur.com/6tZoC.png) + +Artist Search Results (also search by album!): + +![preview thumb](http://i.imgur.com/rIV0P.png) + +Artist Page with Bio & Album Overview: + +![preview thumb](http://i.imgur.com/SSil1.png) + +Album Page with track overview: + +![preview thumb](http://i.imgur.com/kcjES.png) + + +This is free software under the GPL v3 open source license - so feel free to do with it what you wish. diff --git a/data/interfaces/default/css/config.less b/data/interfaces/default/css/config.less index 4fdccaee..022a0700 100644 --- a/data/interfaces/default/css/config.less +++ b/data/interfaces/default/css/config.less @@ -1,4 +1,4 @@ -/* Variables */ +/* Variables */ @base-font-face: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; @alt-font-face: "Trebuchet MS", Helvetica, Arial, sans-serif; @base-font-size: 12px; @@ -10,20 +10,20 @@ @msg-bg: #FFF6A9; @msg-bg-success: #D3FFD7; @msg-bg-error: #FFD3D3; - -/* Mixins */ -.rounded(@radius: 5px) { - -moz-border-radius: @radius; - -webkit-border-radius: @radius; - border-radius: @radius; -} -.roundedTop(@radius: 5px) { - -moz-border-radius-topleft: @radius; - -moz-border-radius-topright: @radius; - -webkit-border-top-right-radius: @radius; - -webkit-border-top-left-radius: @radius; - border-top-left-radius: @radius; - border-top-right-radius: @radius; + +/* Mixins */ +.rounded(@radius: 5px) { + -moz-border-radius: @radius; + -webkit-border-radius: @radius; + border-radius: @radius; +} +.roundedTop(@radius: 5px) { + -moz-border-radius-topleft: @radius; + -moz-border-radius-topright: @radius; + -webkit-border-top-right-radius: @radius; + -webkit-border-top-left-radius: @radius; + border-top-left-radius: @radius; + border-top-right-radius: @radius; } .roundedLeftTop(@radius: 5px) { -moz-border-radius-topleft: @radius; @@ -34,14 +34,14 @@ -moz-border-radius-topright: @radius; -webkit-border-top-right-radius: @radius; border-top-right-radius: @radius; -} -.roundedBottom(@radius: 5px) { - -moz-border-radius-bottomleft: @radius; - -moz-border-radius-bottomright: @radius; - -webkit-border-bottom-right-radius: @radius; - -webkit-border-bottom-left-radius: @radius; - border-bottom-left-radius: @radius; - border-bottom-right-radius: @radius; +} +.roundedBottom(@radius: 5px) { + -moz-border-radius-bottomleft: @radius; + -moz-border-radius-bottomright: @radius; + -webkit-border-bottom-right-radius: @radius; + -webkit-border-bottom-left-radius: @radius; + border-bottom-left-radius: @radius; + border-bottom-right-radius: @radius; } .roundedLeftBottom(@radius: 5px) { -moz-border-radius-bottomleft: @radius; @@ -52,7 +52,7 @@ -moz-border-radius-bottomright: @radius; -webkit-border-bottom-right-radius: @radius; border-bottom-right-radius: @radius; -} +} .shadow(@shadow: 0 17px 11px -1px #ced8d9) { -moz-box-shadow: @shadow; -webkit-box-shadow: @shadow; @@ -74,4 +74,4 @@ -o-opacity:@opacity_percent / 100 !important; opacity:@opacity_percent / 100 !important; } - + diff --git a/data/interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js b/data/interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js index be772753..a8520051 100644 --- a/data/interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js +++ b/data/interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js @@ -1,1156 +1,1156 @@ -/* - * FancyBox - jQuery Plugin - * Simple and fancy lightbox alternative - * - * Examples and documentation at: http://fancybox.net - * - * Copyright (c) 2008 - 2010 Janis Skarnelis - * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated. - * - * Version: 1.3.4 (11/11/2010) - * Requires: jQuery v1.3+ - * - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - */ - -;(function($) { - var tmp, loading, overlay, wrap, outer, content, close, title, nav_left, nav_right, - - selectedIndex = 0, selectedOpts = {}, selectedArray = [], currentIndex = 0, currentOpts = {}, currentArray = [], - - ajaxLoader = null, imgPreloader = new Image(), imgRegExp = /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i, swfRegExp = /[^\.]\.(swf)\s*$/i, - - loadingTimer, loadingFrame = 1, - - titleHeight = 0, titleStr = '', start_pos, final_pos, busy = false, fx = $.extend($('
')[0], { prop: 0 }), - - isIE6 = $.browser.msie && $.browser.version < 7 && !window.XMLHttpRequest, - - /* - * Private methods - */ - - _abort = function() { - loading.hide(); - - imgPreloader.onerror = imgPreloader.onload = null; - - if (ajaxLoader) { - ajaxLoader.abort(); - } - - tmp.empty(); - }, - - _error = function() { - if (false === selectedOpts.onError(selectedArray, selectedIndex, selectedOpts)) { - loading.hide(); - busy = false; - return; - } - - selectedOpts.titleShow = false; - - selectedOpts.width = 'auto'; - selectedOpts.height = 'auto'; - - tmp.html( '

The requested content cannot be loaded.
Please try again later.

' ); - - _process_inline(); - }, - - _start = function() { - var obj = selectedArray[ selectedIndex ], - href, - type, - title, - str, - emb, - ret; - - _abort(); - - selectedOpts = $.extend({}, $.fn.fancybox.defaults, (typeof $(obj).data('fancybox') == 'undefined' ? selectedOpts : $(obj).data('fancybox'))); - - ret = selectedOpts.onStart(selectedArray, selectedIndex, selectedOpts); - - if (ret === false) { - busy = false; - return; - } else if (typeof ret == 'object') { - selectedOpts = $.extend(selectedOpts, ret); - } - - title = selectedOpts.title || (obj.nodeName ? $(obj).attr('title') : obj.title) || ''; - - if (obj.nodeName && !selectedOpts.orig) { - selectedOpts.orig = $(obj).children("img:first").length ? $(obj).children("img:first") : $(obj); - } - - if (title === '' && selectedOpts.orig && selectedOpts.titleFromAlt) { - title = selectedOpts.orig.attr('alt'); - } - - href = selectedOpts.href || (obj.nodeName ? $(obj).attr('href') : obj.href) || null; - - if ((/^(?:javascript)/i).test(href) || href == '#') { - href = null; - } - - if (selectedOpts.type) { - type = selectedOpts.type; - - if (!href) { - href = selectedOpts.content; - } - - } else if (selectedOpts.content) { - type = 'html'; - - } else if (href) { - if (href.match(imgRegExp)) { - type = 'image'; - - } else if (href.match(swfRegExp)) { - type = 'swf'; - - } else if ($(obj).hasClass("iframe")) { - type = 'iframe'; - - } else if (href.indexOf("#") === 0) { - type = 'inline'; - - } else { - type = 'ajax'; - } - } - - if (!type) { - _error(); - return; - } - - if (type == 'inline') { - obj = href.substr(href.indexOf("#")); - type = $(obj).length > 0 ? 'inline' : 'ajax'; - } - - selectedOpts.type = type; - selectedOpts.href = href; - selectedOpts.title = title; - - if (selectedOpts.autoDimensions) { - if (selectedOpts.type == 'html' || selectedOpts.type == 'inline' || selectedOpts.type == 'ajax') { - selectedOpts.width = 'auto'; - selectedOpts.height = 'auto'; - } else { - selectedOpts.autoDimensions = false; - } - } - - if (selectedOpts.modal) { - selectedOpts.overlayShow = true; - selectedOpts.hideOnOverlayClick = false; - selectedOpts.hideOnContentClick = false; - selectedOpts.enableEscapeButton = false; - selectedOpts.showCloseButton = false; - } - - selectedOpts.padding = parseInt(selectedOpts.padding, 10); - selectedOpts.margin = parseInt(selectedOpts.margin, 10); - - tmp.css('padding', (selectedOpts.padding + selectedOpts.margin)); - - $('.fancybox-inline-tmp').unbind('fancybox-cancel').bind('fancybox-change', function() { - $(this).replaceWith(content.children()); - }); - - switch (type) { - case 'html' : - tmp.html( selectedOpts.content ); - _process_inline(); - break; - - case 'inline' : - if ( $(obj).parent().is('#fancybox-content') === true) { - busy = false; - return; - } - - $('
') - .hide() - .insertBefore( $(obj) ) - .bind('fancybox-cleanup', function() { - $(this).replaceWith(content.children()); - }).bind('fancybox-cancel', function() { - $(this).replaceWith(tmp.children()); - }); - - $(obj).appendTo(tmp); - - _process_inline(); - break; - - case 'image': - busy = false; - - $.fancybox.showActivity(); - - imgPreloader = new Image(); - - imgPreloader.onerror = function() { - _error(); - }; - - imgPreloader.onload = function() { - busy = true; - - imgPreloader.onerror = imgPreloader.onload = null; - - _process_image(); - }; - - imgPreloader.src = href; - break; - - case 'swf': - selectedOpts.scrolling = 'no'; - - str = ''; - emb = ''; - - $.each(selectedOpts.swf, function(name, val) { - str += ''; - emb += ' ' + name + '="' + val + '"'; - }); - - str += ''; - - tmp.html(str); - - _process_inline(); - break; - - case 'ajax': - busy = false; - - $.fancybox.showActivity(); - - selectedOpts.ajax.win = selectedOpts.ajax.success; - - ajaxLoader = $.ajax($.extend({}, selectedOpts.ajax, { - url : href, - data : selectedOpts.ajax.data || {}, - error : function(XMLHttpRequest, textStatus, errorThrown) { - if ( XMLHttpRequest.status > 0 ) { - _error(); - } - }, - success : function(data, textStatus, XMLHttpRequest) { - var o = typeof XMLHttpRequest == 'object' ? XMLHttpRequest : ajaxLoader; - if (o.status == 200) { - if ( typeof selectedOpts.ajax.win == 'function' ) { - ret = selectedOpts.ajax.win(href, data, textStatus, XMLHttpRequest); - - if (ret === false) { - loading.hide(); - return; - } else if (typeof ret == 'string' || typeof ret == 'object') { - data = ret; - } - } - - tmp.html( data ); - _process_inline(); - } - } - })); - - break; - - case 'iframe': - _show(); - break; - } - }, - - _process_inline = function() { - var - w = selectedOpts.width, - h = selectedOpts.height; - - if (w.toString().indexOf('%') > -1) { - w = parseInt( ($(window).width() - (selectedOpts.margin * 2)) * parseFloat(w) / 100, 10) + 'px'; - - } else { - w = w == 'auto' ? 'auto' : w + 'px'; - } - - if (h.toString().indexOf('%') > -1) { - h = parseInt( ($(window).height() - (selectedOpts.margin * 2)) * parseFloat(h) / 100, 10) + 'px'; - - } else { - h = h == 'auto' ? 'auto' : h + 'px'; - } - - tmp.wrapInner('
'); - - selectedOpts.width = tmp.width(); - selectedOpts.height = tmp.height(); - - _show(); - }, - - _process_image = function() { - selectedOpts.width = imgPreloader.width; - selectedOpts.height = imgPreloader.height; - - $("").attr({ - 'id' : 'fancybox-img', - 'src' : imgPreloader.src, - 'alt' : selectedOpts.title - }).appendTo( tmp ); - - _show(); - }, - - _show = function() { - var pos, equal; - - loading.hide(); - - if (wrap.is(":visible") && false === currentOpts.onCleanup(currentArray, currentIndex, currentOpts)) { - $.event.trigger('fancybox-cancel'); - - busy = false; - return; - } - - busy = true; - - $(content.add( overlay )).unbind(); - - $(window).unbind("resize.fb scroll.fb"); - $(document).unbind('keydown.fb'); - - if (wrap.is(":visible") && currentOpts.titlePosition !== 'outside') { - wrap.css('height', wrap.height()); - } - - currentArray = selectedArray; - currentIndex = selectedIndex; - currentOpts = selectedOpts; - - if (currentOpts.overlayShow) { - overlay.css({ - 'background-color' : currentOpts.overlayColor, - 'opacity' : currentOpts.overlayOpacity, - 'cursor' : currentOpts.hideOnOverlayClick ? 'pointer' : 'auto', - 'height' : $(document).height() - }); - - if (!overlay.is(':visible')) { - if (isIE6) { - $('select:not(#fancybox-tmp select)').filter(function() { - return this.style.visibility !== 'hidden'; - }).css({'visibility' : 'hidden'}).one('fancybox-cleanup', function() { - this.style.visibility = 'inherit'; - }); - } - - overlay.show(); - } - } else { - overlay.hide(); - } - - final_pos = _get_zoom_to(); - - _process_title(); - - if (wrap.is(":visible")) { - $( close.add( nav_left ).add( nav_right ) ).hide(); - - pos = wrap.position(), - - start_pos = { - top : pos.top, - left : pos.left, - width : wrap.width(), - height : wrap.height() - }; - - equal = (start_pos.width == final_pos.width && start_pos.height == final_pos.height); - - content.fadeTo(currentOpts.changeFade, 0.3, function() { - var finish_resizing = function() { - content.html( tmp.contents() ).fadeTo(currentOpts.changeFade, 1, _finish); - }; - - $.event.trigger('fancybox-change'); - - content - .empty() - .removeAttr('filter') - .css({ - 'border-width' : currentOpts.padding, - 'width' : final_pos.width - currentOpts.padding * 2, - 'height' : selectedOpts.autoDimensions ? 'auto' : final_pos.height - titleHeight - currentOpts.padding * 2 - }); - - if (equal) { - finish_resizing(); - - } else { - fx.prop = 0; - - $(fx).animate({prop: 1}, { - duration : currentOpts.changeSpeed, - easing : currentOpts.easingChange, - step : _draw, - complete : finish_resizing - }); - } - }); - - return; - } - - wrap.removeAttr("style"); - - content.css('border-width', currentOpts.padding); - - if (currentOpts.transitionIn == 'elastic') { - start_pos = _get_zoom_from(); - - content.html( tmp.contents() ); - - wrap.show(); - - if (currentOpts.opacity) { - final_pos.opacity = 0; - } - - fx.prop = 0; - - $(fx).animate({prop: 1}, { - duration : currentOpts.speedIn, - easing : currentOpts.easingIn, - step : _draw, - complete : _finish - }); - - return; - } - - if (currentOpts.titlePosition == 'inside' && titleHeight > 0) { - title.show(); - } - - content - .css({ - 'width' : final_pos.width - currentOpts.padding * 2, - 'height' : selectedOpts.autoDimensions ? 'auto' : final_pos.height - titleHeight - currentOpts.padding * 2 - }) - .html( tmp.contents() ); - - wrap - .css(final_pos) - .fadeIn( currentOpts.transitionIn == 'none' ? 0 : currentOpts.speedIn, _finish ); - }, - - _format_title = function(title) { - if (title && title.length) { - if (currentOpts.titlePosition == 'float') { - return '
' + title + '
'; - } - - return '
' + title + '
'; - } - - return false; - }, - - _process_title = function() { - titleStr = currentOpts.title || ''; - titleHeight = 0; - - title - .empty() - .removeAttr('style') - .removeClass(); - - if (currentOpts.titleShow === false) { - title.hide(); - return; - } - - titleStr = $.isFunction(currentOpts.titleFormat) ? currentOpts.titleFormat(titleStr, currentArray, currentIndex, currentOpts) : _format_title(titleStr); - - if (!titleStr || titleStr === '') { - title.hide(); - return; - } - - title - .addClass('fancybox-title-' + currentOpts.titlePosition) - .html( titleStr ) - .appendTo( 'body' ) - .show(); - - switch (currentOpts.titlePosition) { - case 'inside': - title - .css({ - 'width' : final_pos.width - (currentOpts.padding * 2), - 'marginLeft' : currentOpts.padding, - 'marginRight' : currentOpts.padding - }); - - titleHeight = title.outerHeight(true); - - title.appendTo( outer ); - - final_pos.height += titleHeight; - break; - - case 'over': - title - .css({ - 'marginLeft' : currentOpts.padding, - 'width' : final_pos.width - (currentOpts.padding * 2), - 'bottom' : currentOpts.padding - }) - .appendTo( outer ); - break; - - case 'float': - title - .css('left', parseInt((title.width() - final_pos.width - 40)/ 2, 10) * -1) - .appendTo( wrap ); - break; - - default: - title - .css({ - 'width' : final_pos.width - (currentOpts.padding * 2), - 'paddingLeft' : currentOpts.padding, - 'paddingRight' : currentOpts.padding - }) - .appendTo( wrap ); - break; - } - - title.hide(); - }, - - _set_navigation = function() { - if (currentOpts.enableEscapeButton || currentOpts.enableKeyboardNav) { - $(document).bind('keydown.fb', function(e) { - if (e.keyCode == 27 && currentOpts.enableEscapeButton) { - e.preventDefault(); - $.fancybox.close(); - - } else if ((e.keyCode == 37 || e.keyCode == 39) && currentOpts.enableKeyboardNav && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'SELECT') { - e.preventDefault(); - $.fancybox[ e.keyCode == 37 ? 'prev' : 'next'](); - } - }); - } - - if (!currentOpts.showNavArrows) { - nav_left.hide(); - nav_right.hide(); - return; - } - - if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex !== 0) { - nav_left.show(); - } - - if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex != (currentArray.length -1)) { - nav_right.show(); - } - }, - - _finish = function () { - if (!$.support.opacity) { - content.get(0).style.removeAttribute('filter'); - wrap.get(0).style.removeAttribute('filter'); - } - - if (selectedOpts.autoDimensions) { - content.css('height', 'auto'); - } - - wrap.css('height', 'auto'); - - if (titleStr && titleStr.length) { - title.show(); - } - - if (currentOpts.showCloseButton) { - close.show(); - } - - _set_navigation(); - - if (currentOpts.hideOnContentClick) { - content.bind('click', $.fancybox.close); - } - - if (currentOpts.hideOnOverlayClick) { - overlay.bind('click', $.fancybox.close); - } - - $(window).bind("resize.fb", $.fancybox.resize); - - if (currentOpts.centerOnScroll) { - $(window).bind("scroll.fb", $.fancybox.center); - } - - if (currentOpts.type == 'iframe') { - $('').appendTo(content); - } - - wrap.show(); - - busy = false; - - $.fancybox.center(); - - currentOpts.onComplete(currentArray, currentIndex, currentOpts); - - _preload_images(); - }, - - _preload_images = function() { - var href, - objNext; - - if ((currentArray.length -1) > currentIndex) { - href = currentArray[ currentIndex + 1 ].href; - - if (typeof href !== 'undefined' && href.match(imgRegExp)) { - objNext = new Image(); - objNext.src = href; - } - } - - if (currentIndex > 0) { - href = currentArray[ currentIndex - 1 ].href; - - if (typeof href !== 'undefined' && href.match(imgRegExp)) { - objNext = new Image(); - objNext.src = href; - } - } - }, - - _draw = function(pos) { - var dim = { - width : parseInt(start_pos.width + (final_pos.width - start_pos.width) * pos, 10), - height : parseInt(start_pos.height + (final_pos.height - start_pos.height) * pos, 10), - - top : parseInt(start_pos.top + (final_pos.top - start_pos.top) * pos, 10), - left : parseInt(start_pos.left + (final_pos.left - start_pos.left) * pos, 10) - }; - - if (typeof final_pos.opacity !== 'undefined') { - dim.opacity = pos < 0.5 ? 0.5 : pos; - } - - wrap.css(dim); - - content.css({ - 'width' : dim.width - currentOpts.padding * 2, - 'height' : dim.height - (titleHeight * pos) - currentOpts.padding * 2 - }); - }, - - _get_viewport = function() { - return [ - $(window).width() - (currentOpts.margin * 2), - $(window).height() - (currentOpts.margin * 2), - $(document).scrollLeft() + currentOpts.margin, - $(document).scrollTop() + currentOpts.margin - ]; - }, - - _get_zoom_to = function () { - var view = _get_viewport(), - to = {}, - resize = currentOpts.autoScale, - double_padding = currentOpts.padding * 2, - ratio; - - if (currentOpts.width.toString().indexOf('%') > -1) { - to.width = parseInt((view[0] * parseFloat(currentOpts.width)) / 100, 10); - } else { - to.width = currentOpts.width + double_padding; - } - - if (currentOpts.height.toString().indexOf('%') > -1) { - to.height = parseInt((view[1] * parseFloat(currentOpts.height)) / 100, 10); - } else { - to.height = currentOpts.height + double_padding; - } - - if (resize && (to.width > view[0] || to.height > view[1])) { - if (selectedOpts.type == 'image' || selectedOpts.type == 'swf') { - ratio = (currentOpts.width ) / (currentOpts.height ); - - if ((to.width ) > view[0]) { - to.width = view[0]; - to.height = parseInt(((to.width - double_padding) / ratio) + double_padding, 10); - } - - if ((to.height) > view[1]) { - to.height = view[1]; - to.width = parseInt(((to.height - double_padding) * ratio) + double_padding, 10); - } - - } else { - to.width = Math.min(to.width, view[0]); - to.height = Math.min(to.height, view[1]); - } - } - - to.top = parseInt(Math.max(view[3] - 20, view[3] + ((view[1] - to.height - 40) * 0.5)), 10); - to.left = parseInt(Math.max(view[2] - 20, view[2] + ((view[0] - to.width - 40) * 0.5)), 10); - - return to; - }, - - _get_obj_pos = function(obj) { - var pos = obj.offset(); - - pos.top += parseInt( obj.css('paddingTop'), 10 ) || 0; - pos.left += parseInt( obj.css('paddingLeft'), 10 ) || 0; - - pos.top += parseInt( obj.css('border-top-width'), 10 ) || 0; - pos.left += parseInt( obj.css('border-left-width'), 10 ) || 0; - - pos.width = obj.width(); - pos.height = obj.height(); - - return pos; - }, - - _get_zoom_from = function() { - var orig = selectedOpts.orig ? $(selectedOpts.orig) : false, - from = {}, - pos, - view; - - if (orig && orig.length) { - pos = _get_obj_pos(orig); - - from = { - width : pos.width + (currentOpts.padding * 2), - height : pos.height + (currentOpts.padding * 2), - top : pos.top - currentOpts.padding - 20, - left : pos.left - currentOpts.padding - 20 - }; - - } else { - view = _get_viewport(); - - from = { - width : currentOpts.padding * 2, - height : currentOpts.padding * 2, - top : parseInt(view[3] + view[1] * 0.5, 10), - left : parseInt(view[2] + view[0] * 0.5, 10) - }; - } - - return from; - }, - - _animate_loading = function() { - if (!loading.is(':visible')){ - clearInterval(loadingTimer); - return; - } - - $('div', loading).css('top', (loadingFrame * -40) + 'px'); - - loadingFrame = (loadingFrame + 1) % 12; - }; - - /* - * Public methods - */ - - $.fn.fancybox = function(options) { - if (!$(this).length) { - return this; - } - - $(this) - .data('fancybox', $.extend({}, options, ($.metadata ? $(this).metadata() : {}))) - .unbind('click.fb') - .bind('click.fb', function(e) { - e.preventDefault(); - - if (busy) { - return; - } - - busy = true; - - $(this).blur(); - - selectedArray = []; - selectedIndex = 0; - - var rel = $(this).attr('rel') || ''; - - if (!rel || rel == '' || rel === 'nofollow') { - selectedArray.push(this); - - } else { - selectedArray = $("a[rel=" + rel + "], area[rel=" + rel + "]"); - selectedIndex = selectedArray.index( this ); - } - - _start(); - - return; - }); - - return this; - }; - - $.fancybox = function(obj) { - var opts; - - if (busy) { - return; - } - - busy = true; - opts = typeof arguments[1] !== 'undefined' ? arguments[1] : {}; - - selectedArray = []; - selectedIndex = parseInt(opts.index, 10) || 0; - - if ($.isArray(obj)) { - for (var i = 0, j = obj.length; i < j; i++) { - if (typeof obj[i] == 'object') { - $(obj[i]).data('fancybox', $.extend({}, opts, obj[i])); - } else { - obj[i] = $({}).data('fancybox', $.extend({content : obj[i]}, opts)); - } - } - - selectedArray = jQuery.merge(selectedArray, obj); - - } else { - if (typeof obj == 'object') { - $(obj).data('fancybox', $.extend({}, opts, obj)); - } else { - obj = $({}).data('fancybox', $.extend({content : obj}, opts)); - } - - selectedArray.push(obj); - } - - if (selectedIndex > selectedArray.length || selectedIndex < 0) { - selectedIndex = 0; - } - - _start(); - }; - - $.fancybox.showActivity = function() { - clearInterval(loadingTimer); - - loading.show(); - loadingTimer = setInterval(_animate_loading, 66); - }; - - $.fancybox.hideActivity = function() { - loading.hide(); - }; - - $.fancybox.next = function() { - return $.fancybox.pos( currentIndex + 1); - }; - - $.fancybox.prev = function() { - return $.fancybox.pos( currentIndex - 1); - }; - - $.fancybox.pos = function(pos) { - if (busy) { - return; - } - - pos = parseInt(pos); - - selectedArray = currentArray; - - if (pos > -1 && pos < currentArray.length) { - selectedIndex = pos; - _start(); - - } else if (currentOpts.cyclic && currentArray.length > 1) { - selectedIndex = pos >= currentArray.length ? 0 : currentArray.length - 1; - _start(); - } - - return; - }; - - $.fancybox.cancel = function() { - if (busy) { - return; - } - - busy = true; - - $.event.trigger('fancybox-cancel'); - - _abort(); - - selectedOpts.onCancel(selectedArray, selectedIndex, selectedOpts); - - busy = false; - }; - - // Note: within an iframe use - parent.$.fancybox.close(); - $.fancybox.close = function() { - if (busy || wrap.is(':hidden')) { - return; - } - - busy = true; - - if (currentOpts && false === currentOpts.onCleanup(currentArray, currentIndex, currentOpts)) { - busy = false; - return; - } - - _abort(); - - $(close.add( nav_left ).add( nav_right )).hide(); - - $(content.add( overlay )).unbind(); - - $(window).unbind("resize.fb scroll.fb"); - $(document).unbind('keydown.fb'); - - content.find('iframe').attr('src', isIE6 && /^https/i.test(window.location.href || '') ? 'javascript:void(false)' : 'about:blank'); - - if (currentOpts.titlePosition !== 'inside') { - title.empty(); - } - - wrap.stop(); - - function _cleanup() { - overlay.fadeOut('fast'); - - title.empty().hide(); - wrap.hide(); - - $.event.trigger('fancybox-cleanup'); - - content.empty(); - - currentOpts.onClosed(currentArray, currentIndex, currentOpts); - - currentArray = selectedOpts = []; - currentIndex = selectedIndex = 0; - currentOpts = selectedOpts = {}; - - busy = false; - } - - if (currentOpts.transitionOut == 'elastic') { - start_pos = _get_zoom_from(); - - var pos = wrap.position(); - - final_pos = { - top : pos.top , - left : pos.left, - width : wrap.width(), - height : wrap.height() - }; - - if (currentOpts.opacity) { - final_pos.opacity = 1; - } - - title.empty().hide(); - - fx.prop = 1; - - $(fx).animate({ prop: 0 }, { - duration : currentOpts.speedOut, - easing : currentOpts.easingOut, - step : _draw, - complete : _cleanup - }); - - } else { - wrap.fadeOut( currentOpts.transitionOut == 'none' ? 0 : currentOpts.speedOut, _cleanup); - } - }; - - $.fancybox.resize = function() { - if (overlay.is(':visible')) { - overlay.css('height', $(document).height()); - } - - $.fancybox.center(true); - }; - - $.fancybox.center = function() { - var view, align; - - if (busy) { - return; - } - - align = arguments[0] === true ? 1 : 0; - view = _get_viewport(); - - if (!align && (wrap.width() > view[0] || wrap.height() > view[1])) { - return; - } - - wrap - .stop() - .animate({ - 'top' : parseInt(Math.max(view[3] - 20, view[3] + ((view[1] - content.height() - 40) * 0.5) - currentOpts.padding)), - 'left' : parseInt(Math.max(view[2] - 20, view[2] + ((view[0] - content.width() - 40) * 0.5) - currentOpts.padding)) - }, typeof arguments[0] == 'number' ? arguments[0] : 200); - }; - - $.fancybox.init = function() { - if ($("#fancybox-wrap").length) { - return; - } - - $('body').append( - tmp = $('
'), - loading = $('
'), - overlay = $('
'), - wrap = $('
') - ); - - outer = $('
') - .append('
') - .appendTo( wrap ); - - outer.append( - content = $('
'), - close = $(''), - title = $('
'), - - nav_left = $(''), - nav_right = $('') - ); - - close.click($.fancybox.close); - loading.click($.fancybox.cancel); - - nav_left.click(function(e) { - e.preventDefault(); - $.fancybox.prev(); - }); - - nav_right.click(function(e) { - e.preventDefault(); - $.fancybox.next(); - }); - - if ($.fn.mousewheel) { - wrap.bind('mousewheel.fb', function(e, delta) { - if (busy) { - e.preventDefault(); - - } else if ($(e.target).get(0).clientHeight == 0 || $(e.target).get(0).scrollHeight === $(e.target).get(0).clientHeight) { - e.preventDefault(); - $.fancybox[ delta > 0 ? 'prev' : 'next'](); - } - }); - } - - if (!$.support.opacity) { - wrap.addClass('fancybox-ie'); - } - - if (isIE6) { - loading.addClass('fancybox-ie6'); - wrap.addClass('fancybox-ie6'); - - $('').prependTo(outer); - } - }; - - $.fn.fancybox.defaults = { - padding : 10, - margin : 40, - opacity : false, - modal : false, - cyclic : false, - scrolling : 'auto', // 'auto', 'yes' or 'no' - - width : 560, - height : 340, - - autoScale : true, - autoDimensions : true, - centerOnScroll : false, - - ajax : {}, - swf : { wmode: 'transparent' }, - - hideOnOverlayClick : true, - hideOnContentClick : false, - - overlayShow : true, - overlayOpacity : 0.7, - overlayColor : '#777', - - titleShow : true, - titlePosition : 'float', // 'float', 'outside', 'inside' or 'over' - titleFormat : null, - titleFromAlt : false, - - transitionIn : 'fade', // 'elastic', 'fade' or 'none' - transitionOut : 'fade', // 'elastic', 'fade' or 'none' - - speedIn : 300, - speedOut : 300, - - changeSpeed : 300, - changeFade : 'fast', - - easingIn : 'swing', - easingOut : 'swing', - - showCloseButton : true, - showNavArrows : true, - enableEscapeButton : true, - enableKeyboardNav : true, - - onStart : function(){}, - onCancel : function(){}, - onComplete : function(){}, - onCleanup : function(){}, - onClosed : function(){}, - onError : function(){} - }; - - $(document).ready(function() { - $.fancybox.init(); - }); - +/* + * FancyBox - jQuery Plugin + * Simple and fancy lightbox alternative + * + * Examples and documentation at: http://fancybox.net + * + * Copyright (c) 2008 - 2010 Janis Skarnelis + * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated. + * + * Version: 1.3.4 (11/11/2010) + * Requires: jQuery v1.3+ + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +;(function($) { + var tmp, loading, overlay, wrap, outer, content, close, title, nav_left, nav_right, + + selectedIndex = 0, selectedOpts = {}, selectedArray = [], currentIndex = 0, currentOpts = {}, currentArray = [], + + ajaxLoader = null, imgPreloader = new Image(), imgRegExp = /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i, swfRegExp = /[^\.]\.(swf)\s*$/i, + + loadingTimer, loadingFrame = 1, + + titleHeight = 0, titleStr = '', start_pos, final_pos, busy = false, fx = $.extend($('
')[0], { prop: 0 }), + + isIE6 = $.browser.msie && $.browser.version < 7 && !window.XMLHttpRequest, + + /* + * Private methods + */ + + _abort = function() { + loading.hide(); + + imgPreloader.onerror = imgPreloader.onload = null; + + if (ajaxLoader) { + ajaxLoader.abort(); + } + + tmp.empty(); + }, + + _error = function() { + if (false === selectedOpts.onError(selectedArray, selectedIndex, selectedOpts)) { + loading.hide(); + busy = false; + return; + } + + selectedOpts.titleShow = false; + + selectedOpts.width = 'auto'; + selectedOpts.height = 'auto'; + + tmp.html( '

The requested content cannot be loaded.
Please try again later.

' ); + + _process_inline(); + }, + + _start = function() { + var obj = selectedArray[ selectedIndex ], + href, + type, + title, + str, + emb, + ret; + + _abort(); + + selectedOpts = $.extend({}, $.fn.fancybox.defaults, (typeof $(obj).data('fancybox') == 'undefined' ? selectedOpts : $(obj).data('fancybox'))); + + ret = selectedOpts.onStart(selectedArray, selectedIndex, selectedOpts); + + if (ret === false) { + busy = false; + return; + } else if (typeof ret == 'object') { + selectedOpts = $.extend(selectedOpts, ret); + } + + title = selectedOpts.title || (obj.nodeName ? $(obj).attr('title') : obj.title) || ''; + + if (obj.nodeName && !selectedOpts.orig) { + selectedOpts.orig = $(obj).children("img:first").length ? $(obj).children("img:first") : $(obj); + } + + if (title === '' && selectedOpts.orig && selectedOpts.titleFromAlt) { + title = selectedOpts.orig.attr('alt'); + } + + href = selectedOpts.href || (obj.nodeName ? $(obj).attr('href') : obj.href) || null; + + if ((/^(?:javascript)/i).test(href) || href == '#') { + href = null; + } + + if (selectedOpts.type) { + type = selectedOpts.type; + + if (!href) { + href = selectedOpts.content; + } + + } else if (selectedOpts.content) { + type = 'html'; + + } else if (href) { + if (href.match(imgRegExp)) { + type = 'image'; + + } else if (href.match(swfRegExp)) { + type = 'swf'; + + } else if ($(obj).hasClass("iframe")) { + type = 'iframe'; + + } else if (href.indexOf("#") === 0) { + type = 'inline'; + + } else { + type = 'ajax'; + } + } + + if (!type) { + _error(); + return; + } + + if (type == 'inline') { + obj = href.substr(href.indexOf("#")); + type = $(obj).length > 0 ? 'inline' : 'ajax'; + } + + selectedOpts.type = type; + selectedOpts.href = href; + selectedOpts.title = title; + + if (selectedOpts.autoDimensions) { + if (selectedOpts.type == 'html' || selectedOpts.type == 'inline' || selectedOpts.type == 'ajax') { + selectedOpts.width = 'auto'; + selectedOpts.height = 'auto'; + } else { + selectedOpts.autoDimensions = false; + } + } + + if (selectedOpts.modal) { + selectedOpts.overlayShow = true; + selectedOpts.hideOnOverlayClick = false; + selectedOpts.hideOnContentClick = false; + selectedOpts.enableEscapeButton = false; + selectedOpts.showCloseButton = false; + } + + selectedOpts.padding = parseInt(selectedOpts.padding, 10); + selectedOpts.margin = parseInt(selectedOpts.margin, 10); + + tmp.css('padding', (selectedOpts.padding + selectedOpts.margin)); + + $('.fancybox-inline-tmp').unbind('fancybox-cancel').bind('fancybox-change', function() { + $(this).replaceWith(content.children()); + }); + + switch (type) { + case 'html' : + tmp.html( selectedOpts.content ); + _process_inline(); + break; + + case 'inline' : + if ( $(obj).parent().is('#fancybox-content') === true) { + busy = false; + return; + } + + $('
') + .hide() + .insertBefore( $(obj) ) + .bind('fancybox-cleanup', function() { + $(this).replaceWith(content.children()); + }).bind('fancybox-cancel', function() { + $(this).replaceWith(tmp.children()); + }); + + $(obj).appendTo(tmp); + + _process_inline(); + break; + + case 'image': + busy = false; + + $.fancybox.showActivity(); + + imgPreloader = new Image(); + + imgPreloader.onerror = function() { + _error(); + }; + + imgPreloader.onload = function() { + busy = true; + + imgPreloader.onerror = imgPreloader.onload = null; + + _process_image(); + }; + + imgPreloader.src = href; + break; + + case 'swf': + selectedOpts.scrolling = 'no'; + + str = ''; + emb = ''; + + $.each(selectedOpts.swf, function(name, val) { + str += ''; + emb += ' ' + name + '="' + val + '"'; + }); + + str += ''; + + tmp.html(str); + + _process_inline(); + break; + + case 'ajax': + busy = false; + + $.fancybox.showActivity(); + + selectedOpts.ajax.win = selectedOpts.ajax.success; + + ajaxLoader = $.ajax($.extend({}, selectedOpts.ajax, { + url : href, + data : selectedOpts.ajax.data || {}, + error : function(XMLHttpRequest, textStatus, errorThrown) { + if ( XMLHttpRequest.status > 0 ) { + _error(); + } + }, + success : function(data, textStatus, XMLHttpRequest) { + var o = typeof XMLHttpRequest == 'object' ? XMLHttpRequest : ajaxLoader; + if (o.status == 200) { + if ( typeof selectedOpts.ajax.win == 'function' ) { + ret = selectedOpts.ajax.win(href, data, textStatus, XMLHttpRequest); + + if (ret === false) { + loading.hide(); + return; + } else if (typeof ret == 'string' || typeof ret == 'object') { + data = ret; + } + } + + tmp.html( data ); + _process_inline(); + } + } + })); + + break; + + case 'iframe': + _show(); + break; + } + }, + + _process_inline = function() { + var + w = selectedOpts.width, + h = selectedOpts.height; + + if (w.toString().indexOf('%') > -1) { + w = parseInt( ($(window).width() - (selectedOpts.margin * 2)) * parseFloat(w) / 100, 10) + 'px'; + + } else { + w = w == 'auto' ? 'auto' : w + 'px'; + } + + if (h.toString().indexOf('%') > -1) { + h = parseInt( ($(window).height() - (selectedOpts.margin * 2)) * parseFloat(h) / 100, 10) + 'px'; + + } else { + h = h == 'auto' ? 'auto' : h + 'px'; + } + + tmp.wrapInner('
'); + + selectedOpts.width = tmp.width(); + selectedOpts.height = tmp.height(); + + _show(); + }, + + _process_image = function() { + selectedOpts.width = imgPreloader.width; + selectedOpts.height = imgPreloader.height; + + $("").attr({ + 'id' : 'fancybox-img', + 'src' : imgPreloader.src, + 'alt' : selectedOpts.title + }).appendTo( tmp ); + + _show(); + }, + + _show = function() { + var pos, equal; + + loading.hide(); + + if (wrap.is(":visible") && false === currentOpts.onCleanup(currentArray, currentIndex, currentOpts)) { + $.event.trigger('fancybox-cancel'); + + busy = false; + return; + } + + busy = true; + + $(content.add( overlay )).unbind(); + + $(window).unbind("resize.fb scroll.fb"); + $(document).unbind('keydown.fb'); + + if (wrap.is(":visible") && currentOpts.titlePosition !== 'outside') { + wrap.css('height', wrap.height()); + } + + currentArray = selectedArray; + currentIndex = selectedIndex; + currentOpts = selectedOpts; + + if (currentOpts.overlayShow) { + overlay.css({ + 'background-color' : currentOpts.overlayColor, + 'opacity' : currentOpts.overlayOpacity, + 'cursor' : currentOpts.hideOnOverlayClick ? 'pointer' : 'auto', + 'height' : $(document).height() + }); + + if (!overlay.is(':visible')) { + if (isIE6) { + $('select:not(#fancybox-tmp select)').filter(function() { + return this.style.visibility !== 'hidden'; + }).css({'visibility' : 'hidden'}).one('fancybox-cleanup', function() { + this.style.visibility = 'inherit'; + }); + } + + overlay.show(); + } + } else { + overlay.hide(); + } + + final_pos = _get_zoom_to(); + + _process_title(); + + if (wrap.is(":visible")) { + $( close.add( nav_left ).add( nav_right ) ).hide(); + + pos = wrap.position(), + + start_pos = { + top : pos.top, + left : pos.left, + width : wrap.width(), + height : wrap.height() + }; + + equal = (start_pos.width == final_pos.width && start_pos.height == final_pos.height); + + content.fadeTo(currentOpts.changeFade, 0.3, function() { + var finish_resizing = function() { + content.html( tmp.contents() ).fadeTo(currentOpts.changeFade, 1, _finish); + }; + + $.event.trigger('fancybox-change'); + + content + .empty() + .removeAttr('filter') + .css({ + 'border-width' : currentOpts.padding, + 'width' : final_pos.width - currentOpts.padding * 2, + 'height' : selectedOpts.autoDimensions ? 'auto' : final_pos.height - titleHeight - currentOpts.padding * 2 + }); + + if (equal) { + finish_resizing(); + + } else { + fx.prop = 0; + + $(fx).animate({prop: 1}, { + duration : currentOpts.changeSpeed, + easing : currentOpts.easingChange, + step : _draw, + complete : finish_resizing + }); + } + }); + + return; + } + + wrap.removeAttr("style"); + + content.css('border-width', currentOpts.padding); + + if (currentOpts.transitionIn == 'elastic') { + start_pos = _get_zoom_from(); + + content.html( tmp.contents() ); + + wrap.show(); + + if (currentOpts.opacity) { + final_pos.opacity = 0; + } + + fx.prop = 0; + + $(fx).animate({prop: 1}, { + duration : currentOpts.speedIn, + easing : currentOpts.easingIn, + step : _draw, + complete : _finish + }); + + return; + } + + if (currentOpts.titlePosition == 'inside' && titleHeight > 0) { + title.show(); + } + + content + .css({ + 'width' : final_pos.width - currentOpts.padding * 2, + 'height' : selectedOpts.autoDimensions ? 'auto' : final_pos.height - titleHeight - currentOpts.padding * 2 + }) + .html( tmp.contents() ); + + wrap + .css(final_pos) + .fadeIn( currentOpts.transitionIn == 'none' ? 0 : currentOpts.speedIn, _finish ); + }, + + _format_title = function(title) { + if (title && title.length) { + if (currentOpts.titlePosition == 'float') { + return '
' + title + '
'; + } + + return '
' + title + '
'; + } + + return false; + }, + + _process_title = function() { + titleStr = currentOpts.title || ''; + titleHeight = 0; + + title + .empty() + .removeAttr('style') + .removeClass(); + + if (currentOpts.titleShow === false) { + title.hide(); + return; + } + + titleStr = $.isFunction(currentOpts.titleFormat) ? currentOpts.titleFormat(titleStr, currentArray, currentIndex, currentOpts) : _format_title(titleStr); + + if (!titleStr || titleStr === '') { + title.hide(); + return; + } + + title + .addClass('fancybox-title-' + currentOpts.titlePosition) + .html( titleStr ) + .appendTo( 'body' ) + .show(); + + switch (currentOpts.titlePosition) { + case 'inside': + title + .css({ + 'width' : final_pos.width - (currentOpts.padding * 2), + 'marginLeft' : currentOpts.padding, + 'marginRight' : currentOpts.padding + }); + + titleHeight = title.outerHeight(true); + + title.appendTo( outer ); + + final_pos.height += titleHeight; + break; + + case 'over': + title + .css({ + 'marginLeft' : currentOpts.padding, + 'width' : final_pos.width - (currentOpts.padding * 2), + 'bottom' : currentOpts.padding + }) + .appendTo( outer ); + break; + + case 'float': + title + .css('left', parseInt((title.width() - final_pos.width - 40)/ 2, 10) * -1) + .appendTo( wrap ); + break; + + default: + title + .css({ + 'width' : final_pos.width - (currentOpts.padding * 2), + 'paddingLeft' : currentOpts.padding, + 'paddingRight' : currentOpts.padding + }) + .appendTo( wrap ); + break; + } + + title.hide(); + }, + + _set_navigation = function() { + if (currentOpts.enableEscapeButton || currentOpts.enableKeyboardNav) { + $(document).bind('keydown.fb', function(e) { + if (e.keyCode == 27 && currentOpts.enableEscapeButton) { + e.preventDefault(); + $.fancybox.close(); + + } else if ((e.keyCode == 37 || e.keyCode == 39) && currentOpts.enableKeyboardNav && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'SELECT') { + e.preventDefault(); + $.fancybox[ e.keyCode == 37 ? 'prev' : 'next'](); + } + }); + } + + if (!currentOpts.showNavArrows) { + nav_left.hide(); + nav_right.hide(); + return; + } + + if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex !== 0) { + nav_left.show(); + } + + if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex != (currentArray.length -1)) { + nav_right.show(); + } + }, + + _finish = function () { + if (!$.support.opacity) { + content.get(0).style.removeAttribute('filter'); + wrap.get(0).style.removeAttribute('filter'); + } + + if (selectedOpts.autoDimensions) { + content.css('height', 'auto'); + } + + wrap.css('height', 'auto'); + + if (titleStr && titleStr.length) { + title.show(); + } + + if (currentOpts.showCloseButton) { + close.show(); + } + + _set_navigation(); + + if (currentOpts.hideOnContentClick) { + content.bind('click', $.fancybox.close); + } + + if (currentOpts.hideOnOverlayClick) { + overlay.bind('click', $.fancybox.close); + } + + $(window).bind("resize.fb", $.fancybox.resize); + + if (currentOpts.centerOnScroll) { + $(window).bind("scroll.fb", $.fancybox.center); + } + + if (currentOpts.type == 'iframe') { + $('').appendTo(content); + } + + wrap.show(); + + busy = false; + + $.fancybox.center(); + + currentOpts.onComplete(currentArray, currentIndex, currentOpts); + + _preload_images(); + }, + + _preload_images = function() { + var href, + objNext; + + if ((currentArray.length -1) > currentIndex) { + href = currentArray[ currentIndex + 1 ].href; + + if (typeof href !== 'undefined' && href.match(imgRegExp)) { + objNext = new Image(); + objNext.src = href; + } + } + + if (currentIndex > 0) { + href = currentArray[ currentIndex - 1 ].href; + + if (typeof href !== 'undefined' && href.match(imgRegExp)) { + objNext = new Image(); + objNext.src = href; + } + } + }, + + _draw = function(pos) { + var dim = { + width : parseInt(start_pos.width + (final_pos.width - start_pos.width) * pos, 10), + height : parseInt(start_pos.height + (final_pos.height - start_pos.height) * pos, 10), + + top : parseInt(start_pos.top + (final_pos.top - start_pos.top) * pos, 10), + left : parseInt(start_pos.left + (final_pos.left - start_pos.left) * pos, 10) + }; + + if (typeof final_pos.opacity !== 'undefined') { + dim.opacity = pos < 0.5 ? 0.5 : pos; + } + + wrap.css(dim); + + content.css({ + 'width' : dim.width - currentOpts.padding * 2, + 'height' : dim.height - (titleHeight * pos) - currentOpts.padding * 2 + }); + }, + + _get_viewport = function() { + return [ + $(window).width() - (currentOpts.margin * 2), + $(window).height() - (currentOpts.margin * 2), + $(document).scrollLeft() + currentOpts.margin, + $(document).scrollTop() + currentOpts.margin + ]; + }, + + _get_zoom_to = function () { + var view = _get_viewport(), + to = {}, + resize = currentOpts.autoScale, + double_padding = currentOpts.padding * 2, + ratio; + + if (currentOpts.width.toString().indexOf('%') > -1) { + to.width = parseInt((view[0] * parseFloat(currentOpts.width)) / 100, 10); + } else { + to.width = currentOpts.width + double_padding; + } + + if (currentOpts.height.toString().indexOf('%') > -1) { + to.height = parseInt((view[1] * parseFloat(currentOpts.height)) / 100, 10); + } else { + to.height = currentOpts.height + double_padding; + } + + if (resize && (to.width > view[0] || to.height > view[1])) { + if (selectedOpts.type == 'image' || selectedOpts.type == 'swf') { + ratio = (currentOpts.width ) / (currentOpts.height ); + + if ((to.width ) > view[0]) { + to.width = view[0]; + to.height = parseInt(((to.width - double_padding) / ratio) + double_padding, 10); + } + + if ((to.height) > view[1]) { + to.height = view[1]; + to.width = parseInt(((to.height - double_padding) * ratio) + double_padding, 10); + } + + } else { + to.width = Math.min(to.width, view[0]); + to.height = Math.min(to.height, view[1]); + } + } + + to.top = parseInt(Math.max(view[3] - 20, view[3] + ((view[1] - to.height - 40) * 0.5)), 10); + to.left = parseInt(Math.max(view[2] - 20, view[2] + ((view[0] - to.width - 40) * 0.5)), 10); + + return to; + }, + + _get_obj_pos = function(obj) { + var pos = obj.offset(); + + pos.top += parseInt( obj.css('paddingTop'), 10 ) || 0; + pos.left += parseInt( obj.css('paddingLeft'), 10 ) || 0; + + pos.top += parseInt( obj.css('border-top-width'), 10 ) || 0; + pos.left += parseInt( obj.css('border-left-width'), 10 ) || 0; + + pos.width = obj.width(); + pos.height = obj.height(); + + return pos; + }, + + _get_zoom_from = function() { + var orig = selectedOpts.orig ? $(selectedOpts.orig) : false, + from = {}, + pos, + view; + + if (orig && orig.length) { + pos = _get_obj_pos(orig); + + from = { + width : pos.width + (currentOpts.padding * 2), + height : pos.height + (currentOpts.padding * 2), + top : pos.top - currentOpts.padding - 20, + left : pos.left - currentOpts.padding - 20 + }; + + } else { + view = _get_viewport(); + + from = { + width : currentOpts.padding * 2, + height : currentOpts.padding * 2, + top : parseInt(view[3] + view[1] * 0.5, 10), + left : parseInt(view[2] + view[0] * 0.5, 10) + }; + } + + return from; + }, + + _animate_loading = function() { + if (!loading.is(':visible')){ + clearInterval(loadingTimer); + return; + } + + $('div', loading).css('top', (loadingFrame * -40) + 'px'); + + loadingFrame = (loadingFrame + 1) % 12; + }; + + /* + * Public methods + */ + + $.fn.fancybox = function(options) { + if (!$(this).length) { + return this; + } + + $(this) + .data('fancybox', $.extend({}, options, ($.metadata ? $(this).metadata() : {}))) + .unbind('click.fb') + .bind('click.fb', function(e) { + e.preventDefault(); + + if (busy) { + return; + } + + busy = true; + + $(this).blur(); + + selectedArray = []; + selectedIndex = 0; + + var rel = $(this).attr('rel') || ''; + + if (!rel || rel == '' || rel === 'nofollow') { + selectedArray.push(this); + + } else { + selectedArray = $("a[rel=" + rel + "], area[rel=" + rel + "]"); + selectedIndex = selectedArray.index( this ); + } + + _start(); + + return; + }); + + return this; + }; + + $.fancybox = function(obj) { + var opts; + + if (busy) { + return; + } + + busy = true; + opts = typeof arguments[1] !== 'undefined' ? arguments[1] : {}; + + selectedArray = []; + selectedIndex = parseInt(opts.index, 10) || 0; + + if ($.isArray(obj)) { + for (var i = 0, j = obj.length; i < j; i++) { + if (typeof obj[i] == 'object') { + $(obj[i]).data('fancybox', $.extend({}, opts, obj[i])); + } else { + obj[i] = $({}).data('fancybox', $.extend({content : obj[i]}, opts)); + } + } + + selectedArray = jQuery.merge(selectedArray, obj); + + } else { + if (typeof obj == 'object') { + $(obj).data('fancybox', $.extend({}, opts, obj)); + } else { + obj = $({}).data('fancybox', $.extend({content : obj}, opts)); + } + + selectedArray.push(obj); + } + + if (selectedIndex > selectedArray.length || selectedIndex < 0) { + selectedIndex = 0; + } + + _start(); + }; + + $.fancybox.showActivity = function() { + clearInterval(loadingTimer); + + loading.show(); + loadingTimer = setInterval(_animate_loading, 66); + }; + + $.fancybox.hideActivity = function() { + loading.hide(); + }; + + $.fancybox.next = function() { + return $.fancybox.pos( currentIndex + 1); + }; + + $.fancybox.prev = function() { + return $.fancybox.pos( currentIndex - 1); + }; + + $.fancybox.pos = function(pos) { + if (busy) { + return; + } + + pos = parseInt(pos); + + selectedArray = currentArray; + + if (pos > -1 && pos < currentArray.length) { + selectedIndex = pos; + _start(); + + } else if (currentOpts.cyclic && currentArray.length > 1) { + selectedIndex = pos >= currentArray.length ? 0 : currentArray.length - 1; + _start(); + } + + return; + }; + + $.fancybox.cancel = function() { + if (busy) { + return; + } + + busy = true; + + $.event.trigger('fancybox-cancel'); + + _abort(); + + selectedOpts.onCancel(selectedArray, selectedIndex, selectedOpts); + + busy = false; + }; + + // Note: within an iframe use - parent.$.fancybox.close(); + $.fancybox.close = function() { + if (busy || wrap.is(':hidden')) { + return; + } + + busy = true; + + if (currentOpts && false === currentOpts.onCleanup(currentArray, currentIndex, currentOpts)) { + busy = false; + return; + } + + _abort(); + + $(close.add( nav_left ).add( nav_right )).hide(); + + $(content.add( overlay )).unbind(); + + $(window).unbind("resize.fb scroll.fb"); + $(document).unbind('keydown.fb'); + + content.find('iframe').attr('src', isIE6 && /^https/i.test(window.location.href || '') ? 'javascript:void(false)' : 'about:blank'); + + if (currentOpts.titlePosition !== 'inside') { + title.empty(); + } + + wrap.stop(); + + function _cleanup() { + overlay.fadeOut('fast'); + + title.empty().hide(); + wrap.hide(); + + $.event.trigger('fancybox-cleanup'); + + content.empty(); + + currentOpts.onClosed(currentArray, currentIndex, currentOpts); + + currentArray = selectedOpts = []; + currentIndex = selectedIndex = 0; + currentOpts = selectedOpts = {}; + + busy = false; + } + + if (currentOpts.transitionOut == 'elastic') { + start_pos = _get_zoom_from(); + + var pos = wrap.position(); + + final_pos = { + top : pos.top , + left : pos.left, + width : wrap.width(), + height : wrap.height() + }; + + if (currentOpts.opacity) { + final_pos.opacity = 1; + } + + title.empty().hide(); + + fx.prop = 1; + + $(fx).animate({ prop: 0 }, { + duration : currentOpts.speedOut, + easing : currentOpts.easingOut, + step : _draw, + complete : _cleanup + }); + + } else { + wrap.fadeOut( currentOpts.transitionOut == 'none' ? 0 : currentOpts.speedOut, _cleanup); + } + }; + + $.fancybox.resize = function() { + if (overlay.is(':visible')) { + overlay.css('height', $(document).height()); + } + + $.fancybox.center(true); + }; + + $.fancybox.center = function() { + var view, align; + + if (busy) { + return; + } + + align = arguments[0] === true ? 1 : 0; + view = _get_viewport(); + + if (!align && (wrap.width() > view[0] || wrap.height() > view[1])) { + return; + } + + wrap + .stop() + .animate({ + 'top' : parseInt(Math.max(view[3] - 20, view[3] + ((view[1] - content.height() - 40) * 0.5) - currentOpts.padding)), + 'left' : parseInt(Math.max(view[2] - 20, view[2] + ((view[0] - content.width() - 40) * 0.5) - currentOpts.padding)) + }, typeof arguments[0] == 'number' ? arguments[0] : 200); + }; + + $.fancybox.init = function() { + if ($("#fancybox-wrap").length) { + return; + } + + $('body').append( + tmp = $('
'), + loading = $('
'), + overlay = $('
'), + wrap = $('
') + ); + + outer = $('
') + .append('
') + .appendTo( wrap ); + + outer.append( + content = $('
'), + close = $(''), + title = $('
'), + + nav_left = $(''), + nav_right = $('') + ); + + close.click($.fancybox.close); + loading.click($.fancybox.cancel); + + nav_left.click(function(e) { + e.preventDefault(); + $.fancybox.prev(); + }); + + nav_right.click(function(e) { + e.preventDefault(); + $.fancybox.next(); + }); + + if ($.fn.mousewheel) { + wrap.bind('mousewheel.fb', function(e, delta) { + if (busy) { + e.preventDefault(); + + } else if ($(e.target).get(0).clientHeight == 0 || $(e.target).get(0).scrollHeight === $(e.target).get(0).clientHeight) { + e.preventDefault(); + $.fancybox[ delta > 0 ? 'prev' : 'next'](); + } + }); + } + + if (!$.support.opacity) { + wrap.addClass('fancybox-ie'); + } + + if (isIE6) { + loading.addClass('fancybox-ie6'); + wrap.addClass('fancybox-ie6'); + + $('').prependTo(outer); + } + }; + + $.fn.fancybox.defaults = { + padding : 10, + margin : 40, + opacity : false, + modal : false, + cyclic : false, + scrolling : 'auto', // 'auto', 'yes' or 'no' + + width : 560, + height : 340, + + autoScale : true, + autoDimensions : true, + centerOnScroll : false, + + ajax : {}, + swf : { wmode: 'transparent' }, + + hideOnOverlayClick : true, + hideOnContentClick : false, + + overlayShow : true, + overlayOpacity : 0.7, + overlayColor : '#777', + + titleShow : true, + titlePosition : 'float', // 'float', 'outside', 'inside' or 'over' + titleFormat : null, + titleFromAlt : false, + + transitionIn : 'fade', // 'elastic', 'fade' or 'none' + transitionOut : 'fade', // 'elastic', 'fade' or 'none' + + speedIn : 300, + speedOut : 300, + + changeSpeed : 300, + changeFade : 'fast', + + easingIn : 'swing', + easingOut : 'swing', + + showCloseButton : true, + showNavArrows : true, + enableEscapeButton : true, + enableKeyboardNav : true, + + onStart : function(){}, + onCancel : function(){}, + onComplete : function(){}, + onCleanup : function(){}, + onClosed : function(){}, + onError : function(){} + }; + + $(document).ready(function() { + $.fancybox.init(); + }); + })(jQuery); \ No newline at end of file diff --git a/headphones/classes.py b/headphones/classes.py index cf6534bb..c6b14055 100644 --- a/headphones/classes.py +++ b/headphones/classes.py @@ -1,130 +1,130 @@ -# This file is part of Headphones. -# -# Headphones is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Headphones is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Headphones. If not, see . - -######################################### -## Stolen from Sick-Beard's classes.py ## -######################################### - -import headphones - -import urllib -import datetime - -from common import USER_AGENT - -class HeadphonesURLopener(urllib.FancyURLopener): - version = USER_AGENT - -class AuthURLOpener(HeadphonesURLopener): - """ - URLOpener class that supports http auth without needing interactive password entry. - If the provided username/password don't work it simply fails. - - user: username to use for HTTP auth - pw: password to use for HTTP auth - """ - def __init__(self, user, pw): - self.username = user - self.password = pw - - # remember if we've tried the username/password before - self.numTries = 0 - - # call the base class - urllib.FancyURLopener.__init__(self) - - def prompt_user_passwd(self, host, realm): - """ - Override this function and instead of prompting just give the - username/password that were provided when the class was instantiated. - """ - - # if this is the first try then provide a username/password - if self.numTries == 0: - self.numTries = 1 - return (self.username, self.password) - - # if we've tried before then return blank which cancels the request - else: - return ('', '') - - # this is pretty much just a hack for convenience - def openit(self, url): - self.numTries = 0 - return HeadphonesURLopener.open(self, url) - -class SearchResult: - """ - Represents a search result from an indexer. - """ - - def __init__(self): - self.provider = -1 - - # URL to the NZB/torrent file - self.url = "" - - # used by some providers to store extra info associated with the result - self.extraInfo = [] - - # quality of the release - self.quality = -1 - - # release name - self.name = "" - - def __str__(self): - - if self.provider == None: - return "Invalid provider, unable to print self" - - myString = self.provider.name + " @ " + self.url + "\n" - myString += "Extra Info:\n" - for extra in self.extraInfo: - myString += " " + extra + "\n" - return myString - -class NZBSearchResult(SearchResult): - """ - Regular NZB result with an URL to the NZB - """ - resultType = "nzb" - -class NZBDataSearchResult(SearchResult): - """ - NZB result where the actual NZB XML data is stored in the extraInfo - """ - resultType = "nzbdata" - -class TorrentSearchResult(SearchResult): - """ - Torrent result with an URL to the torrent - """ - resultType = "torrent" - -class Proper: - def __init__(self, name, url, date): - self.name = name - self.url = url - self.date = date - self.provider = None - self.quality = -1 - - self.tvdbid = -1 - self.season = -1 - self.episode = -1 - - def __str__(self): - return str(self.date)+" "+self.name+" "+str(self.season)+"x"+str(self.episode)+" of "+str(self.tvdbid) +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +######################################### +## Stolen from Sick-Beard's classes.py ## +######################################### + +import headphones + +import urllib +import datetime + +from common import USER_AGENT + +class HeadphonesURLopener(urllib.FancyURLopener): + version = USER_AGENT + +class AuthURLOpener(HeadphonesURLopener): + """ + URLOpener class that supports http auth without needing interactive password entry. + If the provided username/password don't work it simply fails. + + user: username to use for HTTP auth + pw: password to use for HTTP auth + """ + def __init__(self, user, pw): + self.username = user + self.password = pw + + # remember if we've tried the username/password before + self.numTries = 0 + + # call the base class + urllib.FancyURLopener.__init__(self) + + def prompt_user_passwd(self, host, realm): + """ + Override this function and instead of prompting just give the + username/password that were provided when the class was instantiated. + """ + + # if this is the first try then provide a username/password + if self.numTries == 0: + self.numTries = 1 + return (self.username, self.password) + + # if we've tried before then return blank which cancels the request + else: + return ('', '') + + # this is pretty much just a hack for convenience + def openit(self, url): + self.numTries = 0 + return HeadphonesURLopener.open(self, url) + +class SearchResult: + """ + Represents a search result from an indexer. + """ + + def __init__(self): + self.provider = -1 + + # URL to the NZB/torrent file + self.url = "" + + # used by some providers to store extra info associated with the result + self.extraInfo = [] + + # quality of the release + self.quality = -1 + + # release name + self.name = "" + + def __str__(self): + + if self.provider == None: + return "Invalid provider, unable to print self" + + myString = self.provider.name + " @ " + self.url + "\n" + myString += "Extra Info:\n" + for extra in self.extraInfo: + myString += " " + extra + "\n" + return myString + +class NZBSearchResult(SearchResult): + """ + Regular NZB result with an URL to the NZB + """ + resultType = "nzb" + +class NZBDataSearchResult(SearchResult): + """ + NZB result where the actual NZB XML data is stored in the extraInfo + """ + resultType = "nzbdata" + +class TorrentSearchResult(SearchResult): + """ + Torrent result with an URL to the torrent + """ + resultType = "torrent" + +class Proper: + def __init__(self, name, url, date): + self.name = name + self.url = url + self.date = date + self.provider = None + self.quality = -1 + + self.tvdbid = -1 + self.season = -1 + self.episode = -1 + + def __str__(self): + return str(self.date)+" "+self.name+" "+str(self.season)+"x"+str(self.episode)+" of "+str(self.tvdbid) diff --git a/headphones/common.py b/headphones/common.py index c195855f..74aa1db8 100644 --- a/headphones/common.py +++ b/headphones/common.py @@ -1,177 +1,177 @@ -# This file is part of Headphones. -# -# Headphones is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Headphones is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Headphones. If not, see . - -''' -Created on Aug 1, 2011 - -@author: Michael -''' -import platform, operator, os, re - -from headphones import version - -#Identify Our Application -USER_AGENT = 'Headphones/-'+version.HEADPHONES_VERSION+' ('+platform.system()+' '+platform.release()+')' - -### Notification Types -NOTIFY_SNATCH = 1 -NOTIFY_DOWNLOAD = 2 - -notifyStrings = {} -notifyStrings[NOTIFY_SNATCH] = "Started Download" -notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished" - -### Release statuses -UNKNOWN = -1 # should never happen -UNAIRED = 1 # releases that haven't dropped yet -SNATCHED = 2 # qualified with quality -WANTED = 3 # releases we don't have but want to get -DOWNLOADED = 4 # qualified with quality -SKIPPED = 5 # releases we don't want -ARCHIVED = 6 # releases that you don't have locally (counts toward download completion stats) -IGNORED = 7 # releases that you don't want included in your download stats -SNATCHED_PROPER = 9 # qualified with quality - -class Quality: - - NONE = 0 - B192 = 1<<1 # 2 - VBR = 1<<2 # 4 - B256 = 1<<3 # 8 - B320 = 1<<4 #16 - FLAC = 1<<5 #32 - - # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere - UNKNOWN = 1<<15 - - qualityStrings = {NONE: "N/A", - UNKNOWN: "Unknown", - B192: "MP3 192", - VBR: "MP3 VBR", - B256: "MP3 256", - B320: "MP3 320", - FLAC: "Flac"} - - statusPrefixes = {DOWNLOADED: "Downloaded", - SNATCHED: "Snatched"} - - @staticmethod - def _getStatusStrings(status): - toReturn = {} - for x in Quality.qualityStrings.keys(): - toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status]+" ("+Quality.qualityStrings[x]+")" - return toReturn - - @staticmethod - def combineQualities(anyQualities, bestQualities): - anyQuality = 0 - bestQuality = 0 - if anyQualities: - anyQuality = reduce(operator.or_, anyQualities) - if bestQualities: - bestQuality = reduce(operator.or_, bestQualities) - return anyQuality | (bestQuality<<16) - - @staticmethod - def splitQuality(quality): - anyQualities = [] - bestQualities = [] - for curQual in Quality.qualityStrings.keys(): - if curQual & quality: - anyQualities.append(curQual) - if curQual<<16 & quality: - bestQualities.append(curQual) - - return (anyQualities, bestQualities) - - @staticmethod - def nameQuality(name): - - name = os.path.basename(name) - - # if we have our exact text then assume we put it there - for x in Quality.qualityStrings: - if x == Quality.UNKNOWN: - continue - - regex = '\W'+Quality.qualityStrings[x].replace(' ','\W')+'\W' - regex_match = re.search(regex, name, re.I) - if regex_match: - return x - - checkName = lambda list, func: func([re.search(x, name, re.I) for x in list]) - - #TODO: fix quality checking here - if checkName(["mp3", "192"], any) and not checkName(["flac"], all): - return Quality.B192 - elif checkName(["mp3", "256"], any) and not checkName(["flac"], all): - return Quality.B256 - elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all): - return Quality.VBR - elif checkName(["mp3", "320"], any) and not checkName(["flac"], all): - return Quality.B320 - else: - return Quality.UNKNOWN - - @staticmethod - def assumeQuality(name): - - if name.lower().endswith(".mp3"): - return Quality.MP3 - elif name.lower().endswith(".flac"): - return Quality.LOSSLESS - else: - return Quality.UNKNOWN - - @staticmethod - def compositeStatus(status, quality): - return status + 100 * quality - - @staticmethod - def qualityDownloaded(status): - return (status - DOWNLOADED) / 100 - - @staticmethod - def splitCompositeStatus(status): - """Returns a tuple containing (status, quality)""" - for x in sorted(Quality.qualityStrings.keys(), reverse=True): - if status > x*100: - return (status-x*100, x) - - return (Quality.NONE, status) - - @staticmethod - def statusFromName(name, assume=True): - quality = Quality.nameQuality(name) - if assume and quality == Quality.UNKNOWN: - quality = Quality.assumeQuality(name) - return Quality.compositeStatus(DOWNLOADED, quality) - - DOWNLOADED = None - SNATCHED = None - SNATCHED_PROPER = None - -Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()] -Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()] -Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()] - -MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], []) -LOSSLESS = Quality.combineQualities([Quality.FLAC], []) -ANY = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR, Quality.FLAC], []) - -qualityPresets = (MP3, LOSSLESS, ANY) -qualityPresetStrings = {MP3: "MP3 (All bitrates 192+)", - LOSSLESS: "Lossless (flac)", - ANY: "Any"} +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +''' +Created on Aug 1, 2011 + +@author: Michael +''' +import platform, operator, os, re + +from headphones import version + +#Identify Our Application +USER_AGENT = 'Headphones/-'+version.HEADPHONES_VERSION+' ('+platform.system()+' '+platform.release()+')' + +### Notification Types +NOTIFY_SNATCH = 1 +NOTIFY_DOWNLOAD = 2 + +notifyStrings = {} +notifyStrings[NOTIFY_SNATCH] = "Started Download" +notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished" + +### Release statuses +UNKNOWN = -1 # should never happen +UNAIRED = 1 # releases that haven't dropped yet +SNATCHED = 2 # qualified with quality +WANTED = 3 # releases we don't have but want to get +DOWNLOADED = 4 # qualified with quality +SKIPPED = 5 # releases we don't want +ARCHIVED = 6 # releases that you don't have locally (counts toward download completion stats) +IGNORED = 7 # releases that you don't want included in your download stats +SNATCHED_PROPER = 9 # qualified with quality + +class Quality: + + NONE = 0 + B192 = 1<<1 # 2 + VBR = 1<<2 # 4 + B256 = 1<<3 # 8 + B320 = 1<<4 #16 + FLAC = 1<<5 #32 + + # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere + UNKNOWN = 1<<15 + + qualityStrings = {NONE: "N/A", + UNKNOWN: "Unknown", + B192: "MP3 192", + VBR: "MP3 VBR", + B256: "MP3 256", + B320: "MP3 320", + FLAC: "Flac"} + + statusPrefixes = {DOWNLOADED: "Downloaded", + SNATCHED: "Snatched"} + + @staticmethod + def _getStatusStrings(status): + toReturn = {} + for x in Quality.qualityStrings.keys(): + toReturn[Quality.compositeStatus(status, x)] = Quality.statusPrefixes[status]+" ("+Quality.qualityStrings[x]+")" + return toReturn + + @staticmethod + def combineQualities(anyQualities, bestQualities): + anyQuality = 0 + bestQuality = 0 + if anyQualities: + anyQuality = reduce(operator.or_, anyQualities) + if bestQualities: + bestQuality = reduce(operator.or_, bestQualities) + return anyQuality | (bestQuality<<16) + + @staticmethod + def splitQuality(quality): + anyQualities = [] + bestQualities = [] + for curQual in Quality.qualityStrings.keys(): + if curQual & quality: + anyQualities.append(curQual) + if curQual<<16 & quality: + bestQualities.append(curQual) + + return (anyQualities, bestQualities) + + @staticmethod + def nameQuality(name): + + name = os.path.basename(name) + + # if we have our exact text then assume we put it there + for x in Quality.qualityStrings: + if x == Quality.UNKNOWN: + continue + + regex = '\W'+Quality.qualityStrings[x].replace(' ','\W')+'\W' + regex_match = re.search(regex, name, re.I) + if regex_match: + return x + + checkName = lambda list, func: func([re.search(x, name, re.I) for x in list]) + + #TODO: fix quality checking here + if checkName(["mp3", "192"], any) and not checkName(["flac"], all): + return Quality.B192 + elif checkName(["mp3", "256"], any) and not checkName(["flac"], all): + return Quality.B256 + elif checkName(["mp3", "vbr"], any) and not checkName(["flac"], all): + return Quality.VBR + elif checkName(["mp3", "320"], any) and not checkName(["flac"], all): + return Quality.B320 + else: + return Quality.UNKNOWN + + @staticmethod + def assumeQuality(name): + + if name.lower().endswith(".mp3"): + return Quality.MP3 + elif name.lower().endswith(".flac"): + return Quality.LOSSLESS + else: + return Quality.UNKNOWN + + @staticmethod + def compositeStatus(status, quality): + return status + 100 * quality + + @staticmethod + def qualityDownloaded(status): + return (status - DOWNLOADED) / 100 + + @staticmethod + def splitCompositeStatus(status): + """Returns a tuple containing (status, quality)""" + for x in sorted(Quality.qualityStrings.keys(), reverse=True): + if status > x*100: + return (status-x*100, x) + + return (Quality.NONE, status) + + @staticmethod + def statusFromName(name, assume=True): + quality = Quality.nameQuality(name) + if assume and quality == Quality.UNKNOWN: + quality = Quality.assumeQuality(name) + return Quality.compositeStatus(DOWNLOADED, quality) + + DOWNLOADED = None + SNATCHED = None + SNATCHED_PROPER = None + +Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()] +Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()] +Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()] + +MP3 = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR], []) +LOSSLESS = Quality.combineQualities([Quality.FLAC], []) +ANY = Quality.combineQualities([Quality.B192, Quality.B256, Quality.B320, Quality.VBR, Quality.FLAC], []) + +qualityPresets = (MP3, LOSSLESS, ANY) +qualityPresetStrings = {MP3: "MP3 (All bitrates 192+)", + LOSSLESS: "Lossless (flac)", + ANY: "Any"} diff --git a/headphones/exceptions.py b/headphones/exceptions.py index 47de6d99..ecbaa035 100644 --- a/headphones/exceptions.py +++ b/headphones/exceptions.py @@ -1,41 +1,41 @@ -# This file is part of Headphones. -# -# Headphones is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Headphones is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Headphones. If not, see . - -def ex(e): - """ - Returns a string from the exception text if it exists. - """ - - # sanity check - if not e.args or not e.args[0]: - return "" - - e_message = e.args[0] - - # if fixStupidEncodings doesn't fix it then maybe it's not a string, in which case we'll try printing it anyway - if not e_message: - try: - e_message = str(e.args[0]) - except: - e_message = "" - - return e_message - - -class HeadphonesException(Exception): - "Generic Headphones Exception - should never be thrown, only subclassed" - -class NewzbinAPIThrottled(HeadphonesException): - "Newzbin has throttled us, deal with it" +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +def ex(e): + """ + Returns a string from the exception text if it exists. + """ + + # sanity check + if not e.args or not e.args[0]: + return "" + + e_message = e.args[0] + + # if fixStupidEncodings doesn't fix it then maybe it's not a string, in which case we'll try printing it anyway + if not e_message: + try: + e_message = str(e.args[0]) + except: + e_message = "" + + return e_message + + +class HeadphonesException(Exception): + "Generic Headphones Exception - should never be thrown, only subclassed" + +class NewzbinAPIThrottled(HeadphonesException): + "Newzbin has throttled us, deal with it" diff --git a/headphones/sab.py b/headphones/sab.py index a577f020..5249e914 100644 --- a/headphones/sab.py +++ b/headphones/sab.py @@ -1,162 +1,162 @@ -# This file is part of Headphones. -# -# Headphones is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Headphones is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Headphones. If not, see . - -##################################### -## Stolen from Sick-Beard's sab.py ## -##################################### - -import urllib, httplib -import datetime - -import headphones - -from lib import MultipartPostHandler -import urllib2, cookielib -import ast - -from headphones.common import USER_AGENT -from headphones import logger -from headphones import notifiers, helpers - -def sendNZB(nzb): - - params = {} - - if headphones.SAB_USERNAME: - params['ma_username'] = headphones.SAB_USERNAME - if headphones.SAB_PASSWORD: - params['ma_password'] = headphones.SAB_PASSWORD - if headphones.SAB_APIKEY: - params['apikey'] = headphones.SAB_APIKEY - if headphones.SAB_CATEGORY: - params['cat'] = headphones.SAB_CATEGORY - - # if it's a normal result we just pass SAB the URL - if nzb.resultType == "nzb": - # for newzbin results send the ID to sab specifically - if nzb.provider.getID() == 'newzbin': - id = nzb.provider.getIDFromURL(nzb.url) - if not id: - logger.info("Unable to send NZB to sab, can't find ID in URL "+str(nzb.url)) - return False - params['mode'] = 'addid' - params['name'] = id - else: - params['mode'] = 'addurl' - params['name'] = nzb.url - - # if we get a raw data result we want to upload it to SAB - elif nzb.resultType == "nzbdata": - # Sanitize the file a bit, since we can only use ascii chars with MultiPartPostHandler - nzbdata = helpers.latinToAscii(nzb.extraInfo[0]) - params['mode'] = 'addfile' - multiPartParams = {"nzbfile": (nzb.name+".nzb", nzbdata)} - - if not headphones.SAB_HOST.startswith('http'): - headphones.SAB_HOST = 'http://' + headphones.SAB_HOST - - if headphones.SAB_HOST.endswith('/'): - headphones.SAB_HOST = headphones.SAB_HOST[0:len(headphones.SAB_HOST)-1] - - url = headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params) - - try: - - if nzb.resultType == "nzb": - f = urllib.urlopen(url) - elif nzb.resultType == "nzbdata": - cookies = cookielib.CookieJar() - opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), - MultipartPostHandler.MultipartPostHandler) - - req = urllib2.Request(url, - multiPartParams, - headers={'User-Agent': USER_AGENT}) - - f = opener.open(req) - - except (EOFError, IOError), e: - logger.error(u"Unable to connect to SAB with URL: %s" % url) - return False - - except httplib.InvalidURL, e: - logger.error(u"Invalid SAB host, check your config. Current host: %s" % headphones.SAB_HOST) - return False - - except Exception, e: - logger.error(u"Error: " + str(e)) - return False - - if f == None: - logger.info(u"No data returned from SABnzbd, NZB not sent") - return False - - try: - result = f.readlines() - except Exception, e: - logger.info(u"Error trying to get result from SAB, NZB not sent: ") - return False - - if len(result) == 0: - logger.info(u"No data returned from SABnzbd, NZB not sent") - return False - - sabText = result[0].strip() - - logger.info(u"Result text from SAB: " + sabText) - - if sabText == "ok": - logger.info(u"NZB sent to SAB successfully") - return True - elif sabText == "Missing authentication": - logger.info(u"Incorrect username/password sent to SAB, NZB not sent") - return False - else: - logger.info(u"Unknown failure sending NZB to sab. Return text is: " + sabText) - return False - -def checkConfig(): - - params = { 'mode' : 'get_config', - 'section' : 'misc' - } - - if headphones.SAB_USERNAME: - params['ma_username'] = headphones.SAB_USERNAME - if headphones.SAB_PASSWORD: - params['ma_password'] = headphones.SAB_PASSWORD - if headphones.SAB_APIKEY: - params['apikey'] = headphones.SAB_APIKEY - - if not headphones.SAB_HOST.startswith('http'): - headphones.SAB_HOST = 'http://' + headphones.SAB_HOST - - if headphones.SAB_HOST.endswith('/'): - headphones.SAB_HOST = headphones.SAB_HOST[0:len(headphones.SAB_HOST)-1] - - url = headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params) - - try: - f = urllib.urlopen(url).read() - except Exception, e: - logger.warn("Unable to read SABnzbd config file - cannot determine renaming options (might affect auto & forced post processing)") - return (0, 0) - - config_options = ast.literal_eval(f) - - replace_spaces = config_options['misc']['replace_spaces'] - replace_dots = config_options['misc']['replace_dots'] - - return (replace_spaces, replace_dots) +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +##################################### +## Stolen from Sick-Beard's sab.py ## +##################################### + +import urllib, httplib +import datetime + +import headphones + +from lib import MultipartPostHandler +import urllib2, cookielib +import ast + +from headphones.common import USER_AGENT +from headphones import logger +from headphones import notifiers, helpers + +def sendNZB(nzb): + + params = {} + + if headphones.SAB_USERNAME: + params['ma_username'] = headphones.SAB_USERNAME + if headphones.SAB_PASSWORD: + params['ma_password'] = headphones.SAB_PASSWORD + if headphones.SAB_APIKEY: + params['apikey'] = headphones.SAB_APIKEY + if headphones.SAB_CATEGORY: + params['cat'] = headphones.SAB_CATEGORY + + # if it's a normal result we just pass SAB the URL + if nzb.resultType == "nzb": + # for newzbin results send the ID to sab specifically + if nzb.provider.getID() == 'newzbin': + id = nzb.provider.getIDFromURL(nzb.url) + if not id: + logger.info("Unable to send NZB to sab, can't find ID in URL "+str(nzb.url)) + return False + params['mode'] = 'addid' + params['name'] = id + else: + params['mode'] = 'addurl' + params['name'] = nzb.url + + # if we get a raw data result we want to upload it to SAB + elif nzb.resultType == "nzbdata": + # Sanitize the file a bit, since we can only use ascii chars with MultiPartPostHandler + nzbdata = helpers.latinToAscii(nzb.extraInfo[0]) + params['mode'] = 'addfile' + multiPartParams = {"nzbfile": (nzb.name+".nzb", nzbdata)} + + if not headphones.SAB_HOST.startswith('http'): + headphones.SAB_HOST = 'http://' + headphones.SAB_HOST + + if headphones.SAB_HOST.endswith('/'): + headphones.SAB_HOST = headphones.SAB_HOST[0:len(headphones.SAB_HOST)-1] + + url = headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params) + + try: + + if nzb.resultType == "nzb": + f = urllib.urlopen(url) + elif nzb.resultType == "nzbdata": + cookies = cookielib.CookieJar() + opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), + MultipartPostHandler.MultipartPostHandler) + + req = urllib2.Request(url, + multiPartParams, + headers={'User-Agent': USER_AGENT}) + + f = opener.open(req) + + except (EOFError, IOError), e: + logger.error(u"Unable to connect to SAB with URL: %s" % url) + return False + + except httplib.InvalidURL, e: + logger.error(u"Invalid SAB host, check your config. Current host: %s" % headphones.SAB_HOST) + return False + + except Exception, e: + logger.error(u"Error: " + str(e)) + return False + + if f == None: + logger.info(u"No data returned from SABnzbd, NZB not sent") + return False + + try: + result = f.readlines() + except Exception, e: + logger.info(u"Error trying to get result from SAB, NZB not sent: ") + return False + + if len(result) == 0: + logger.info(u"No data returned from SABnzbd, NZB not sent") + return False + + sabText = result[0].strip() + + logger.info(u"Result text from SAB: " + sabText) + + if sabText == "ok": + logger.info(u"NZB sent to SAB successfully") + return True + elif sabText == "Missing authentication": + logger.info(u"Incorrect username/password sent to SAB, NZB not sent") + return False + else: + logger.info(u"Unknown failure sending NZB to sab. Return text is: " + sabText) + return False + +def checkConfig(): + + params = { 'mode' : 'get_config', + 'section' : 'misc' + } + + if headphones.SAB_USERNAME: + params['ma_username'] = headphones.SAB_USERNAME + if headphones.SAB_PASSWORD: + params['ma_password'] = headphones.SAB_PASSWORD + if headphones.SAB_APIKEY: + params['apikey'] = headphones.SAB_APIKEY + + if not headphones.SAB_HOST.startswith('http'): + headphones.SAB_HOST = 'http://' + headphones.SAB_HOST + + if headphones.SAB_HOST.endswith('/'): + headphones.SAB_HOST = headphones.SAB_HOST[0:len(headphones.SAB_HOST)-1] + + url = headphones.SAB_HOST + "/" + "api?" + urllib.urlencode(params) + + try: + f = urllib.urlopen(url).read() + except Exception, e: + logger.warn("Unable to read SABnzbd config file - cannot determine renaming options (might affect auto & forced post processing)") + return (0, 0) + + config_options = ast.literal_eval(f) + + replace_spaces = config_options['misc']['replace_spaces'] + replace_dots = config_options['misc']['replace_dots'] + + return (replace_spaces, replace_dots) diff --git a/lib/MultipartPostHandler.py b/lib/MultipartPostHandler.py index 82fa59c6..ef63afa2 100644 --- a/lib/MultipartPostHandler.py +++ b/lib/MultipartPostHandler.py @@ -1,88 +1,88 @@ -#!/usr/bin/python - -#### -# 06/2010 Nic Wolfe -# 02/2006 Will Holcomb -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# - -import urllib -import urllib2 -import mimetools, mimetypes -import os, sys - -# Controls how sequences are uncoded. If true, elements may be given multiple values by -# assigning a sequence. -doseq = 1 - -class MultipartPostHandler(urllib2.BaseHandler): - handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first - - def http_request(self, request): - data = request.get_data() - if data is not None and type(data) != str: - v_files = [] - v_vars = [] - try: - for(key, value) in data.items(): - if type(value) in (file, list, tuple): - v_files.append((key, value)) - else: - v_vars.append((key, value)) - except TypeError: - systype, value, traceback = sys.exc_info() - raise TypeError, "not a valid non-string sequence or mapping object", traceback - - if len(v_files) == 0: - data = urllib.urlencode(v_vars, doseq) - else: - boundary, data = MultipartPostHandler.multipart_encode(v_vars, v_files) - contenttype = 'multipart/form-data; boundary=%s' % boundary - if(request.has_header('Content-Type') - and request.get_header('Content-Type').find('multipart/form-data') != 0): - print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data') - request.add_unredirected_header('Content-Type', contenttype) - - request.add_data(data) - return request - - @staticmethod - def multipart_encode(vars, files, boundary = None, buffer = None): - if boundary is None: - boundary = mimetools.choose_boundary() - if buffer is None: - buffer = '' - for(key, value) in vars: - buffer += '--%s\r\n' % boundary - buffer += 'Content-Disposition: form-data; name="%s"' % key - buffer += '\r\n\r\n' + value + '\r\n' - for(key, fd) in files: - - # allow them to pass in a file or a tuple with name & data - if type(fd) == file: - name_in = fd.name - fd.seek(0) - data_in = fd.read() - elif type(fd) in (tuple, list): - name_in, data_in = fd - - filename = os.path.basename(name_in) - contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' - buffer += '--%s\r\n' % boundary - buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename) - buffer += 'Content-Type: %s\r\n' % contenttype - # buffer += 'Content-Length: %s\r\n' % file_size - buffer += '\r\n' + data_in + '\r\n' - buffer += '--%s--\r\n\r\n' % boundary - return boundary, buffer - +#!/usr/bin/python + +#### +# 06/2010 Nic Wolfe +# 02/2006 Will Holcomb +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# + +import urllib +import urllib2 +import mimetools, mimetypes +import os, sys + +# Controls how sequences are uncoded. If true, elements may be given multiple values by +# assigning a sequence. +doseq = 1 + +class MultipartPostHandler(urllib2.BaseHandler): + handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first + + def http_request(self, request): + data = request.get_data() + if data is not None and type(data) != str: + v_files = [] + v_vars = [] + try: + for(key, value) in data.items(): + if type(value) in (file, list, tuple): + v_files.append((key, value)) + else: + v_vars.append((key, value)) + except TypeError: + systype, value, traceback = sys.exc_info() + raise TypeError, "not a valid non-string sequence or mapping object", traceback + + if len(v_files) == 0: + data = urllib.urlencode(v_vars, doseq) + else: + boundary, data = MultipartPostHandler.multipart_encode(v_vars, v_files) + contenttype = 'multipart/form-data; boundary=%s' % boundary + if(request.has_header('Content-Type') + and request.get_header('Content-Type').find('multipart/form-data') != 0): + print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data') + request.add_unredirected_header('Content-Type', contenttype) + + request.add_data(data) + return request + + @staticmethod + def multipart_encode(vars, files, boundary = None, buffer = None): + if boundary is None: + boundary = mimetools.choose_boundary() + if buffer is None: + buffer = '' + for(key, value) in vars: + buffer += '--%s\r\n' % boundary + buffer += 'Content-Disposition: form-data; name="%s"' % key + buffer += '\r\n\r\n' + value + '\r\n' + for(key, fd) in files: + + # allow them to pass in a file or a tuple with name & data + if type(fd) == file: + name_in = fd.name + fd.seek(0) + data_in = fd.read() + elif type(fd) in (tuple, list): + name_in, data_in = fd + + filename = os.path.basename(name_in) + contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + buffer += '--%s\r\n' % boundary + buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename) + buffer += 'Content-Type: %s\r\n' % contenttype + # buffer += 'Content-Length: %s\r\n' % file_size + buffer += '\r\n' + data_in + '\r\n' + buffer += '--%s--\r\n\r\n' % boundary + return boundary, buffer + https_request = http_request \ No newline at end of file diff --git a/lib/__init__.py b/lib/__init__.py index d3f5a12f..8b137891 100755 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1 +1 @@ - + diff --git a/lib/cherrypy/LICENSE.txt b/lib/cherrypy/LICENSE.txt index 8db13fb2..3816f933 100644 --- a/lib/cherrypy/LICENSE.txt +++ b/lib/cherrypy/LICENSE.txt @@ -1,25 +1,25 @@ -Copyright (c) 2004-2011, CherryPy Team (team@cherrypy.org) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the CherryPy Team nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2004-2011, CherryPy Team (team@cherrypy.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the CherryPy Team nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/ordereddict.py b/lib/ordereddict.py index 5b0303f5..7242b506 100644 --- a/lib/ordereddict.py +++ b/lib/ordereddict.py @@ -1,127 +1,127 @@ -# Copyright (c) 2009 Raymond Hettinger -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -from UserDict import DictMixin - -class OrderedDict(dict, DictMixin): - - def __init__(self, *args, **kwds): - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__end - except AttributeError: - self.clear() - self.update(*args, **kwds) - - def clear(self): - self.__end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.__map = {} # key --> [key, prev, next] - dict.clear(self) - - def __setitem__(self, key, value): - if key not in self: - end = self.__end - curr = end[1] - curr[2] = end[1] = self.__map[key] = [key, curr, end] - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - key, prev, next = self.__map.pop(key) - prev[2] = next - next[1] = prev - - def __iter__(self): - end = self.__end - curr = end[2] - while curr is not end: - yield curr[0] - curr = curr[2] - - def __reversed__(self): - end = self.__end - curr = end[1] - while curr is not end: - yield curr[0] - curr = curr[1] - - def popitem(self, last=True): - if not self: - raise KeyError('dictionary is empty') - if last: - key = reversed(self).next() - else: - key = iter(self).next() - value = self.pop(key) - return key, value - - def __reduce__(self): - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__end - del self.__map, self.__end - inst_dict = vars(self).copy() - self.__map, self.__end = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def keys(self): - return list(self) - - setdefault = DictMixin.setdefault - update = DictMixin.update - pop = DictMixin.pop - values = DictMixin.values - items = DictMixin.items - iterkeys = DictMixin.iterkeys - itervalues = DictMixin.itervalues - iteritems = DictMixin.iteritems - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - - def copy(self): - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - if isinstance(other, OrderedDict): - if len(self) != len(other): - return False - for p, q in zip(self.items(), other.items()): - if p != q: - return False - return True - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other