From: Marcin Koziej Date: Thu, 26 Apr 2012 09:45:11 +0000 (+0200) Subject: html preview imported from WL X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/3e0073d9716160a04b18ef2fbb8723d9d42b7a24 html preview imported from WL --- diff --git a/apps/catalogue/templates/catalogue/book_text.html b/apps/catalogue/templates/catalogue/book_text.html new file mode 100644 index 00000000..dc7a80d2 --- /dev/null +++ b/apps/catalogue/templates/catalogue/book_text.html @@ -0,0 +1,32 @@ +{% load i18n %} + + + + + {% trans "Redakcja" %} :: {{ book.title }} + + + + + + + + + + + + {{ html|safe }} + + diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 2ee94aea..d0213daa 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -12,12 +12,13 @@ from django.core.urlresolvers import reverse from django.db.models import Count, Q from django import http from django.http import Http404, HttpResponse, HttpResponseForbidden -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, render, render_to_response from django.utils.encoding import iri_to_uri from django.utils.http import urlquote_plus from django.utils.translation import ugettext_lazy as _ from django.views.decorators.http import require_POST from django.views.generic.simple import direct_to_template +from django.template import RequestContext from apiclient import NotAuthorizedError from catalogue import forms @@ -225,10 +226,20 @@ def book_html(request, slug): return HttpResponseForbidden("Not authorized.") doc = book.wldocument(parse_dublincore=False) - html = doc.as_html(flags=['full-page']) + html = doc.as_html() + html = html.get_string() if html is not None else '' - response = http.HttpResponse(html, content_type='text/html', mimetype='text/html') - return response + # response = http.HttpResponse(html, content_type='text/html', mimetype='text/html') + # return response + # book_themes = {} + # for fragment in book.fragments.all().iterator(): + # for theme in fragment.tags.filter(category='theme').iterator(): + # book_themes.setdefault(theme, []).append(fragment) + + # book_themes = book_themes.items() + # book_themes.sort(key=lambda s: s[0].sort_key) + return render_to_response('catalogue/book_text.html', locals(), + context_instance=RequestContext(request)) @never_cache diff --git a/redakcja/static/css/book.css b/redakcja/static/css/book.css new file mode 100644 index 00000000..354a8396 --- /dev/null +++ b/redakcja/static/css/book.css @@ -0,0 +1,406 @@ +body { + font-size: 16px; + font: Georgia, "Times New Roman", serif; + line-height: 1.5em; + margin: 0; +} + +a { + color: blue; + text-decoration: none; +} + +#book-text { + margin: 3em; + max-width: 36em; +} + +/* ================================== */ +/* = Header with logo and menu = */ +/* ================================== */ +#header { + margin: 3.4em 0 0 1.4em; +} + +img { + border: none; +} + +#logo { + font-size: 1.5em; +} +#logo a { + color: black; +} + +#menu { + position: fixed; + left: 0em; + top: 0em; + width: 100%; + height: 1.5em; + background: #333; + color: #FFF; + opacity: 0.9; + z-index: 99; +} + +#menu ul { + list-style: none; + padding: 0; + margin: 0; +} + +#menu li a { + display: block; + float: left; + height: 1.5em; + margin-left: 0.5em; + text-align: center; + color: #FFF; + padding: 0 1em; +} +#menu li a.menu { + padding-right: 1.5em; +} + +#menu li a.menu:hover, #menu li a.menu:active { + color: #000; + background: #FFF url(/media/static/img/arrow-down.png) no-repeat center right; +} + +#menu li a.menu.selected { + color: #000; + background: #FFF url(/media/static/img/arrow-up.png) no-repeat center right; +} +#menu a.menu-link { + display: block; + float: left; + height: 1.5em; + margin-left: 0.5em; + text-align: center; + color: #FFF; +} +#menu span { + color: #888; + font-style: italic; + font-size: .75em; + margin-right: 0.5em; +} + + +#toc, #themes, #nota_red, #info { + position: fixed; + left: 0em; + top: 1.5em; + width: 37em; + padding: 1.5em; + background: #FFF; + border-bottom: 0.25em solid #DDD; + border-right: 0.25em solid #DDD; + display: none; + height: 16em; + overflow-x: hidden; + overflow-y: auto; + opacity: 0.9; + z-index: 99; +} +#download { + position: fixed; + left: 0em; + top: 1.5em; + width: 37em; + padding: 1.5em; + background: #FFF; + border-bottom: 0.25em solid #DDD; + border-right: 0.25em solid #DDD; + display: none; + height: 10em; + overflow-x: hidden; + overflow-y: auto; + opacity: 0.9; + z-index: 99; +} + +#toc ol, #themes ol { + list-style: none; + padding: 0; + margin: 0; +} + +#toc ol li { + font-weight: bold; +} + +#toc ol ol { + padding: 0 0 1.5em 1.5em; + margin: 0; +} + +#toc ol ol li { + font-weight: normal; +} + +#toc h2 { + display: none; +} + +#toc .anchor { + float: none; + margin: 0; + color: blue; + font-size: 16px; + position: inherit; +} + +#info p { + text-align: justify; + margin: 1.5em 0 0; +} + +/* =================================================== */ +/* = Common elements: headings, paragraphs and lines = */ +/* =================================================== */ +h1 { + font-size: 3em; + margin: 1.5em 0; + text-align: center; + line-height: 1.5em; + font-weight: bold; +} + +h2 { + font-size: 2em; + margin: 1.5em 0 0; + font-weight: bold; + line-height: 1.5em; +} + +h3 { + font-size: 1.5em; + margin: 1.5em 0 0; + font-weight: normal; + line-height: 1.5em; +} + +h4 { + font-size: 1em; + margin: 1.5em 0 0; + line-height: 1.5em; +} + +p { + margin: 0; +} + +/* ======================== */ +/* = Footnotes and themes = */ +/* ======================== */ +.theme-begin { + border-left: 0.1em solid #DDDDDD; + color: #777; + padding: 0 0.5em; + width: 7.5em; + + font-style: normal; + font-weight: normal; + font-variant: normal; + letter-spacing: 0; + text-transform: none; + text-decoration: none; + + font-size: 16px; + float: right; + margin-right: -9.5em; + margin-bottom: 0.5em; + clear: both; + left: 40em; + line-height: 1.5em; + text-align: left; +} + +.annotation { + font-style: normal; + font-weight: normal; + font-size: 12px; + padding-left: 2px; + position: relative; + top: -4px; +} + +#footnotes { + margin-top: 3em; +} + +#footnotes .annotation { + display: block; + float: left; + width: 2.5em; + clear: both; +} + +#footnotes div { + margin: 1.5em 0 0 0; +} + +#footnotes p, #footnotes ul { + margin-left: 2.5em; + font-size: 0.875em; +} + +#footnotes .permalink { + font-size: .75em; +} + +blockquote { + font-size: 0.875em; +} + +/* ============= */ +/* = Numbering = */ +/* ============= */ +.verse, .paragraph { + position:relative; +} +.anchor { + position: absolute; + margin: -0.25em -0.5em; + left: -3em; + color: #777; + font-size: 12px; + width: 2em; + text-align: center; + padding: 0.25em 0.5em; + line-height: 1.5em; +} + +.anchor:hover, #book-text .anchor:active { + color: #FFF; + background-color: #CCC; +} + +/* =================== */ +/* = Custom elements = */ +/* =================== */ +span.author { + font-size: 0.5em; + display: block; + line-height: 1.5em; + margin-bottom: 0.25em; +} + +span.collection { + font-size: 0.375em; + display: block; + line-height: 1.5em; + margin-bottom: -0.25em; +} + +span.subtitle { + font-size: 0.5em; + display: block; + line-height: 1.5em; + margin-top: -0.25em; +} + +span.translator { + font-size: 0.375em; + display: block; + line-height: 1.5em; + margin-top: 0.25em; +} + +div.didaskalia { + font-style: italic; + margin: 0.5em 0 0 1.5em; +} + +div.kwestia { + margin: 0.5em 0 0; +} + +div.stanza { + margin: 1.5em 0 0; +} + +div.kwestia div.stanza { + margin: 0; +} + +p.paragraph { + text-align: justify; + margin: 1.5em 0 0; +} + +p.motto { + text-align: justify; + font-style: italic; + margin: 1.5em 0 0; +} + +p.motto_podpis { + font-size: 0.875em; + text-align: right; +} + +div.fragment { + border-bottom: 0.1em solid #999; + padding-bottom: 1.5em; +} + +div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph { + text-align: right; + font-style: italic; +} + +hr.spacer { + height: 3em; + visibility: hidden; +} + +hr.spacer-line { + margin: 1.5em 0; + border: none; + border-bottom: 0.1em solid #000; +} + +p.spacer-asterisk { + padding: 0; + margin: 1.5em 0; + text-align: center; +} + +div.person-list ol { + list-style: none; + padding: 0 0 0 1.5em; +} + +p.place-and-time { + font-style: italic; +} + +em.math, em.foreign-word, em.book-title, em.didaskalia { + font-style: italic; +} + +em.author-emphasis { + letter-spacing: 0.1em; +} + +em.person { + font-style: normal; + font-variant: small-caps; +} + +.verse:after { + content: "\feff"; +} + + +/* =================================== */ +/* = Hide some elements for printing = */ +/* =================================== */ + +@media print { + #menu {display: none;} +} diff --git a/redakcja/static/icons/revert_.png b/redakcja/static/icons/revert_.png new file mode 100755 index 00000000..afdf20d7 Binary files /dev/null and b/redakcja/static/icons/revert_.png differ diff --git a/redakcja/static/img/arrow-down.png b/redakcja/static/img/arrow-down.png new file mode 100644 index 00000000..0e32315c Binary files /dev/null and b/redakcja/static/img/arrow-down.png differ diff --git a/redakcja/static/img/arrow-up.png b/redakcja/static/img/arrow-up.png new file mode 100644 index 00000000..cdf9cf63 Binary files /dev/null and b/redakcja/static/img/arrow-up.png differ diff --git a/redakcja/static/img/logo-220.png b/redakcja/static/img/logo-220.png new file mode 100644 index 00000000..9b15e88a Binary files /dev/null and b/redakcja/static/img/logo-220.png differ diff --git a/redakcja/static/js/book_text/book.js b/redakcja/static/js/book_text/book.js new file mode 100644 index 00000000..335fe39c --- /dev/null +++ b/redakcja/static/js/book_text/book.js @@ -0,0 +1,61 @@ +$(function() { + function scrollToAnchor(anchor) { + if (anchor) { + var anchor_name = anchor.slice(1); + var element = $('a[name="' + anchor_name + '"]'); + if (element.length > 0) { + $.scrollTo(element, 500, {offset: {top: -50, left: 0}}); + foot_elem = $('#footnotes a[name="' + anchor_name + '"]'); + if (foot_elem.length > 0) { + $(element).parent().highlightFade('yellow'); + } + window.location.hash = anchor; + } + } + } + + $.highlightFade.defaults.speed = 3000; + $('#toc').hide(); + if ($('#toc li').length == 0) { + $('#menu li a[href="#toc"]').remove(); + } + if ($('#nota_red').length == 0) { + $('#menu li a[href="#nota_red"]').remove(); + } + + // On page load, scroll to anchor + scrollToAnchor(window.location.hash) + + $('#toc, #themes, #book-text').delegate('click', 'a', function(event) { + event.preventDefault(); + $('#menu li a.selected').click(); + scrollToAnchor($(this).attr('href')); + }); + + $('#menu li a.menu').toggle(function() { + $('#menu li a.selected').click(); + $(this).addClass('selected'); + $($(this).attr('href')).slideDown('fast'); + }, function() { + $(this).removeClass('selected'); + $($(this).attr('href')).slideUp('fast'); + }); + + + if (window.getSelection) { + $('.theme-begin').click(function() { + var selection = window.getSelection(); + selection.removeAllRanges(); + var range = document.createRange(); + + var e = $(".theme-end[fid='" + $(this).attr('fid') + "']")[0]; + + if (e) { + range.setStartAfter(this); + range.setEndBefore(e); + selection.addRange(range); + } + }); + } + +}); diff --git a/redakcja/static/js/book_text/jquery.eventdelegation.js b/redakcja/static/js/book_text/jquery.eventdelegation.js new file mode 100644 index 00000000..52fce074 --- /dev/null +++ b/redakcja/static/js/book_text/jquery.eventdelegation.js @@ -0,0 +1,55 @@ +/* + * jQuery Event Delegation Plugin - jquery.eventdelegation.js + * Fast flexible event handling + * + * January 2008 - Randy Morey (http://dev.distilldesign.com/) + */ + +(function ($) { + /* setup list of allowed events for event delegation + * only events that bubble are appropriate + */ + var allowed = {}; + $.each([ + 'click', + 'dblclick', + 'mousedown', + 'mouseup', + 'mousemove', + 'mouseover', + 'mouseout', + 'keydown', + 'keypress', + 'keyup' + ], function(i, eventName) { + allowed[eventName] = true; + }); + + $.fn.extend({ + delegate: function (event, selector, f) { + return $(this).each(function () { + if (allowed[event]) + $(this).bind(event, function (e) { + var el = $(e.target), + result = false; + + while (!$(el).is('body')) { + if ($(el).is(selector)) { + result = f.apply($(el)[0], [e]); + if (result === false) + e.preventDefault(); + return; + } + + el = $(el).parent(); + } + }); + }); + }, + undelegate: function (event) { + return $(this).each(function () { + $(this).unbind(event); + }); + } + }); +})(jQuery); \ No newline at end of file diff --git a/redakcja/static/js/book_text/jquery.highlightfade.js b/redakcja/static/js/book_text/jquery.highlightfade.js new file mode 100644 index 00000000..bbe39f07 --- /dev/null +++ b/redakcja/static/js/book_text/jquery.highlightfade.js @@ -0,0 +1,150 @@ +/** + * jQuery Plugin highlightFade (jquery.offput.ca/highlightFade) + * (c) 2006 Blair Mitchelmore (offput.ca) blair@offput.ca + */ +/** + * This is version 0.7 of my highlightFade plugin. It follows the yellow fade technique of Web 2.0 fame + * but expands it to allow any starting colour and allows you to specify the end colour as well. + * + * For the moment, I'm done with this plug-in. Unless I come upon a really cool feature it should have + * this plug-in will only receive updates to ensure future compatibility with jQuery. + * + * As of now (Aug. 16, 2006) the plugin has been written with the 1.0.1 release of jQuery (rev 249) which + * is available from http://jquery.com/src/jquery-1.0.1.js + * + * A note regarding rgb() syntax: I noticed that most browsers implement rgb syntax as either an integer + * (0-255) or percentage (0-100%) value for each field, that is, rgb(i/p,i/p,i/p); however, the W3C + * standard clearly defines it as "either three integer values or three percentage values" [http://www.w3.org/TR/CSS21/syndata.html] + * which I choose to follow despite the error redundancy of the typical behaviour browsers employ. + * + * Changelog: + * + * 0.7: + * - Added the awesome custom attribute support written by George Adamson (slightly modified) + * - Removed bgColor plugin dependency seeing as attr is customizable now... + * 0.6: + * - Abstracted getBGColor into its own plugin with optional test and data retrieval functions + * - Converted all $ references to jQuery references as John's code seems to be shifting away + * from that and I don't want to have to update this for a long time. + * 0.5: + * - Added simple argument syntax for only specifying start colour of event + * - Removed old style argument syntax + * - Added 'interval', 'final, and 'end' properties + * - Renamed 'color' property to 'start' + * - Added second argument to $.highlightFade.getBGColor to bypass the e.highlighting check + * 0.4: + * - Added rgb(%,%,%) color syntax + * 0.3: + * - Fixed bug when event was called while parent was also running event corrupting the + * the background colour of the child + * 0.2: + * - Fixed bug where an unspecified onComplete function made the page throw continuous errors + * - Fixed bug where multiple events on the same element would speed each subsequent event + * 0.1: + * - Initial Release + * + * @author Blair Mitchelmore (blair@offput.ca) + * @version 0.5 + */ +jQuery.fn.highlightFade = function(settings) { + var o = (settings && settings.constructor == String) ? {start: settings} : settings || {}; + var d = jQuery.highlightFade.defaults; + var i = o['interval'] || d['interval']; + var a = o['attr'] || d['attr']; + var ts = { + 'linear': function(s,e,t,c) { return parseInt(s+(c/t)*(e-s)); }, + 'sinusoidal': function(s,e,t,c) { return parseInt(s+Math.sin(((c/t)*90)*(Math.PI/180))*(e-s)); }, + 'exponential': function(s,e,t,c) { return parseInt(s+(Math.pow(c/t,2))*(e-s)); } + }; + var t = (o['iterator'] && o['iterator'].constructor == Function) ? o['iterator'] : ts[o['iterator']] || ts[d['iterator']] || ts['linear']; + if (d['iterator'] && d['iterator'].constructor == Function) t = d['iterator']; + return this.each(function() { + if (!this.highlighting) this.highlighting = {}; + var e = (this.highlighting[a]) ? this.highlighting[a].end : jQuery.highlightFade.getBaseValue(this,a) || [255,255,255]; + var c = jQuery.highlightFade.getRGB(o['start'] || o['colour'] || o['color'] || d['start'] || [255,255,128]); + var s = jQuery.speed(o['speed'] || d['speed']); + var r = o['final'] || (this.highlighting[a] && this.highlighting[a].orig) ? this.highlighting[a].orig : jQuery.curCSS(this,a); + if (o['end'] || d['end']) r = jQuery.highlightFade.asRGBString(e = jQuery.highlightFade.getRGB(o['end'] || d['end'])); + if (typeof o['final'] != 'undefined') r = o['final']; + if (this.highlighting[a] && this.highlighting[a].timer) window.clearInterval(this.highlighting[a].timer); + this.highlighting[a] = { steps: ((s.duration) / i), interval: i, currentStep: 0, start: c, end: e, orig: r, attr: a }; + jQuery.highlightFade(this,a,o['complete'],t); + }); +}; + +jQuery.highlightFade = function(e,a,o,t) { + e.highlighting[a].timer = window.setInterval(function() { + var newR = t(e.highlighting[a].start[0],e.highlighting[a].end[0],e.highlighting[a].steps,e.highlighting[a].currentStep); + var newG = t(e.highlighting[a].start[1],e.highlighting[a].end[1],e.highlighting[a].steps,e.highlighting[a].currentStep); + var newB = t(e.highlighting[a].start[2],e.highlighting[a].end[2],e.highlighting[a].steps,e.highlighting[a].currentStep); + jQuery(e).css(a,jQuery.highlightFade.asRGBString([newR,newG,newB])); + if (e.highlighting[a].currentStep++ >= e.highlighting[a].steps) { + jQuery(e).css(a,e.highlighting[a].orig || ''); + window.clearInterval(e.highlighting[a].timer); + e.highlighting[a] = null; + if (o && o.constructor == Function) o.call(e); + } + },e.highlighting[a].interval); +}; + +jQuery.highlightFade.defaults = { + start: [255,255,128], + interval: 50, + speed: 400, + attr: 'backgroundColor' +}; + +jQuery.highlightFade.getRGB = function(c,d) { + var result; + if (c && c.constructor == Array && c.length == 3) return c; + if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)) + return [parseInt(result[1]),parseInt(result[2]),parseInt(result[3])]; + else if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)) + return [parseFloat(result[1])*2.55,parseFloat(result[2])*2.55,parseFloat(result[3])*2.55]; + else if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)) + return [parseInt("0x" + result[1]),parseInt("0x" + result[2]),parseInt("0x" + result[3])]; + else if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)) + return [parseInt("0x"+ result[1] + result[1]),parseInt("0x" + result[2] + result[2]),parseInt("0x" + result[3] + result[3])]; + else + return jQuery.highlightFade.checkColorName(c) || d || null; +}; + +jQuery.highlightFade.asRGBString = function(a) { + return "rgb(" + a.join(",") + ")"; +}; + +jQuery.highlightFade.getBaseValue = function(e,a,b) { + var s, t; + b = b || false; + t = a = a || jQuery.highlightFade.defaults['attr']; + do { + s = jQuery(e).css(t || 'backgroundColor'); + if ((s != '' && s != 'transparent') || (e.tagName.toLowerCase() == "body") || (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end)) break; + t = false; + } while (e = e.parentNode); + if (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end) s = e.highlighting[a].end; + if (s == undefined || s == '' || s == 'transparent') s = [255,255,255]; + return jQuery.highlightFade.getRGB(s); +}; + +jQuery.highlightFade.checkColorName = function(c) { + if (!c) return null; + switch(c.replace(/^\s*|\s*$/g,'').toLowerCase()) { + case 'aqua': return [0,255,255]; + case 'black': return [0,0,0]; + case 'blue': return [0,0,255]; + case 'fuchsia': return [255,0,255]; + case 'gray': return [128,128,128]; + case 'green': return [0,128,0]; + case 'lime': return [0,255,0]; + case 'maroon': return [128,0,0]; + case 'navy': return [0,0,128]; + case 'olive': return [128,128,0]; + case 'purple': return [128,0,128]; + case 'red': return [255,0,0]; + case 'silver': return [192,192,192]; + case 'teal': return [0,128,128]; + case 'white': return [255,255,255]; + case 'yellow': return [255,255,0]; + } +}; diff --git a/redakcja/static/js/book_text/jquery.scrollto.js b/redakcja/static/js/book_text/jquery.scrollto.js new file mode 100644 index 00000000..c403ab9d --- /dev/null +++ b/redakcja/static/js/book_text/jquery.scrollto.js @@ -0,0 +1,194 @@ +/** + * jQuery.ScrollTo + * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 9/11/2008 + * + * @projectDescription Easy element scrolling using jQuery. + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + * Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows. + * + * @author Ariel Flesler + * @version 1.4 + * + * @id jQuery.scrollTo + * @id jQuery.fn.scrollTo + * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. + * The different options for target are: + * - A number position (will be applied to all axes). + * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes + * - A jQuery/DOM element ( logically, child of the element to scroll ) + * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) + * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. + * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. + * @param {Object,Function} settings Optional set of settings or the onAfter callback. + * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. + * @option {Number} duration The OVERALL length of the animation. + * @option {String} easing The easing method for the animation. + * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. + * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. + * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. + * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. + * @option {Function} onAfter Function to be called after the scrolling ends. + * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. + * @return {jQuery} Returns the same jQuery object, for chaining. + * + * @desc Scroll to a fixed position + * @example $('div').scrollTo( 340 ); + * + * @desc Scroll relatively to the actual position + * @example $('div').scrollTo( '+=340px', { axis:'y' } ); + * + * @dec Scroll using a selector (relative to the scrolled element) + * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); + * + * @ Scroll to a DOM element (same for jQuery object) + * @example var second_child = document.getElementById('container').firstChild.nextSibling; + * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ + * alert('scrolled!!'); + * }}); + * + * @desc Scroll on both axes, to different values + * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); + */ +;(function( $ ){ + + var $scrollTo = $.scrollTo = function( target, duration, settings ){ + $(window).scrollTo( target, duration, settings ); + }; + + $scrollTo.defaults = { + axis:'y', + duration:1 + }; + + // Returns the element that needs to be animated to scroll the window. + // Kept for backwards compatibility (specially for localScroll & serialScroll) + $scrollTo.window = function( scope ){ + return $(window).scrollable(); + }; + + // Hack, hack, hack... stay away! + // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) + $.fn.scrollable = function(){ + return this.map(function(){ + // Just store it, we might need it + var win = this.parentWindow || this.defaultView, + // If it's a document, get its iframe or the window if it's THE document + elem = this.nodeName == '#document' ? win.frameElement || win : this, + // Get the corresponding document + doc = elem.contentDocument || (elem.contentWindow || elem).document, + isWin = elem.setInterval; + + return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body + : isWin ? doc.documentElement + : this; + }); + }; + + $.fn.scrollTo = function( target, duration, settings ){ + if( typeof duration == 'object' ){ + settings = duration; + duration = 0; + } + if( typeof settings == 'function' ) + settings = { onAfter:settings }; + + settings = $.extend( {}, $scrollTo.defaults, settings ); + // Speed is still recognized for backwards compatibility + duration = duration || settings.speed || settings.duration; + // Make sure the settings are given right + settings.queue = settings.queue && settings.axis.length > 1; + + if( settings.queue ) + // Let's keep the overall duration + duration /= 2; + settings.offset = both( settings.offset ); + settings.over = both( settings.over ); + + return this.scrollable().each(function(){ + var elem = this, + $elem = $(elem), + targ = target, toff, attr = {}, + win = $elem.is('html,body'); + + switch( typeof targ ){ + // A number will pass the regex + case 'number': + case 'string': + if( /^([+-]=)?\d+(px)?$/.test(targ) ){ + targ = both( targ ); + // We are done + break; + } + // Relative selector, no break! + targ = $(targ,this); + case 'object': + // DOMElement / jQuery + if( targ.is || targ.style ) + // Get the real position of the target + toff = (targ = $(targ)).offset(); + } + $.each( settings.axis.split(''), function( i, axis ){ + var Pos = axis == 'x' ? 'Left' : 'Top', + pos = Pos.toLowerCase(), + key = 'scroll' + Pos, + old = elem[key], + Dim = axis == 'x' ? 'Width' : 'Height', + dim = Dim.toLowerCase(); + + if( toff ){// jQuery / DOMElement + attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); + + // If it's a dom element, reduce the margin + if( settings.margin ){ + attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; + attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; + } + + attr[key] += settings.offset[pos] || 0; + + if( settings.over[pos] ) + // Scroll to a fraction of its width/height + attr[key] += targ[dim]() * settings.over[pos]; + }else + attr[key] = targ[pos]; + + // Number or 'number' + if( /^\d+$/.test(attr[key]) ) + // Check the limits + attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) ); + + // Queueing axes + if( !i && settings.queue ){ + // Don't waste time animating, if there's no need. + if( old != attr[key] ) + // Intermediate animation + animate( settings.onAfterFirst ); + // Don't animate this axis again in the next iteration. + delete attr[key]; + } + }); + animate( settings.onAfter ); + + function animate( callback ){ + $elem.animate( attr, duration, settings.easing, callback && function(){ + callback.call(this, target, settings); + }); + }; + function max( Dim ){ + var attr ='scroll'+Dim, + doc = elem.ownerDocument; + + return win + ? Math.max( doc.documentElement[attr], doc.body[attr] ) + : elem[attr]; + }; + }).end(); + }; + + function both( val ){ + return typeof val == 'object' ? val : { top:val, left:val }; + }; + +})( jQuery ); \ No newline at end of file