[svn] added lodgeit sourcecode

This commit is contained in:
blackbird 2006-12-15 22:21:59 +01:00
commit 56d1d3d9cd
40 changed files with 7737 additions and 0 deletions

0
pastebin/__init__.py Normal file
View File

View File

@ -0,0 +1,220 @@
#
# lodgeit.rb
#
# Copyright (C) 2006 Armin Ronacher
#
# Author: Armin Ronacher
# License: GNU GPL
require 'xmlrpc/client'
# == LodgeIt API
#
# This class provides access to the lodge it pastebin
# at http://paste.pocoo.org/
#
# == Examples
#
# === Basic Queries
#
# Example #1: Create LodgeIt instance
#
# require 'lodgeit'
# a = LodgeIt.new
#
# Example #2: Query LodgeIt
#
# puts a.paste_count
# puts a.private_count
#
# The latter should output the total count of pastes in the
# lodgeit pastebin, the latter the number of private pastes.
class LodgeIt
PASTEBIN_URL = 'http://paste.pocoo.org/'
SERVICE_URL = "#{PASTEBIN_URL}xmlrpc/"
def initialize
@service = XMLRPC::Client.new2(SERVICE_URL)
@_languages = nil
end
def inspect
'#<LodgeIt>'
end
# Number of pastes
def paste_count
@service.call('pastes.countPastes')
end
# number of private pastes
def private_count
@service.call('pastes.countPrivate')
end
# number of public pastes
def public_count
@service.call('pastes.countPublic')
end
# hash of supported languages
def languages
if @_languages.nil?
h = Hash.new
@service.call('pastes.getLanguages').each do |key, value|
h[key.to_sym] = value
end
@_languages = h
end
@_languages
end
# this method checks if a language exists
def language_exists? alias_
rv = self.get_name_by_alias(alias_.to_s)
not rv.nil?
end
# get the alias for a filename
def get_alias_for_filename filename
rv = @service.call('pastes.getAliasForFilename', filename)
(rv.empty?) ? nil : rv
end
# get the alias for a mimetype
def get_alias_for_mimetype mimetype
rv = @service.call('pastes.getAliasForMimetype', mimetype)
(rv.empty?) ? nil : rv
end
# get the name for an alias
def get_name_by_alias alias_
rv = @service.call('pastes.getNameByAlias', alias_)
(rv.empty?) ? nil : rv
end
# return the paste "uid"
def get_paste uid
rv = @service.call('pastes.getPaste', uid)
if not rv
return nil
end
LodgeIt::Paste.new(self, rv)
end
# return the last "n" pastes
def get_recent_pastes n
@service.call('pastes.getRecent', n).map do |data|
LodgeIt::Paste.new(self, data)
end
end
# return the most recent paste
def get_recent_paste
self.get_recent_pastes(1).first
end
# return all pastes for a given tag name
def get_pastes_for_tag tag
@service.call('pastes.getPastesForTag', tag).map do |data|
LodgeIt::Paste.new(self, data)
end
end
# return the url or a paste
def get_paste_url uid
rv = @service.call('pastes.getURL', uid)
(rv.empty?) ? nil : rv
end
# create a new paste
def new_paste code, language='text', private_=false, title='Untitled',
author='anonymous', tags=[]
if language != 'text' and not self.language_exists(language)
raise AttributeError, "unknown language '#{language}'"
end
rv = @service.call('pastes.newPaste', language, code, private_,
title, author, tags)
if rv == 0
return nil
end
Paste.new(self, rv['uid'])
end
# check if a file exists
def style_exists? style
@service.call('styles.styleExists', style.to_s)
end
# return a list of supported styles
def get_style_list
@service.call('styles.getStyleList').map { |x| x.to_sym }
end
# return the css file of a name or nil if the style does
# not exist. If prefix is given, all css definitions will
# be prefixed with it (eg: "div.syntax")
def get_style style, prefix=''
rv = @service.call('styles.getStyle', style, prefix)
(rv.empty?) ? nil : rv
end
# return a tag cloud. The return values is a hash with
# the following keys:
#
# name name of the tag
# size size of the tag in pixels
# count number of pastes tagged with this tag
def get_tag_cloud
@service.call('tags.getTagCloud')
end
# == Paste
#
# This class represents a paste. You should not create instances
# of this class yourself.
class Paste
attr_reader(:uid, :title, :author, :private, :pub_date,
:code, :parsed_code, :language, :language_name,
:url, :tags)
def initialize agent, data
@agent = agent
@uid = data['uid']
@title = data['title']
@author = data['author']
@private = data['private'] || false
@pub_date = Time.at(data['pub_date'])
@code = data['code']
@parsed_code = data['parsed_code']
@language = data['language']
@language_name = data['language_name']
@url = data['url']
@tags = data['tags']
@reply_to = (data['reply_to'].empty?) ? nil : data['reply_to']
# used as cache for reply_to
@_reply_to = nil
end
def inspect
"#<LodgeIt::Paste '#{@uid}'>"
end
def reply_to
if not @reply_to.nil?
if not @_reply_to
@_reply_to = @agent.get_paste(@reply_to)
end
return @_reply_to
end
nil
end
end
end

299
pastebin/lodgeit.py Executable file
View File

@ -0,0 +1,299 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LodgeIt!
~~~~~~~~
A script that pastes stuff into the lodgeit pastebin on
paste.pocoo.org.
.lodgeitrc / _lodgeitrc
-----------------------
Under UNIX create a file called ``~/.lodgerc``, under Windows
create a file ``%APPDATA%/_lodgerc`` to override defaults::
author=Your Name
language=default_language
private=true/false
clipboard=true/false
open_browser=true/false
encoding=fallback_charset
:authors: 2006 Armin Ronacher <armin.ronacher@active-4.com>,
2006 Matt Good <matt@matt-good.net>,
2005 Raphael Slinckx <raphael@slinckx.net>
"""
import os
import sys
SCRIPT_NAME = os.path.basename(sys.argv[0])
VERSION = '0.1'
SERVICE_URL = 'http://paste.pocoo.org/xmlrpc/'
SETTING_KEYS = ['author', 'title', 'language', 'private', 'clipboard',
'open_browser']
def fail(msg, code):
print >> sys.stderr, 'ERROR: %s' % msg
sys.exit(code)
def load_default_settings():
"""Load the defaults from the lodgeitrc file."""
settings = {
'author': None,
'language': None,
'private': False,
'clipboard': True,
'open_browser': False,
'encoding': 'iso-8859-15'
}
rcfile = None
if os.name == 'posix':
rcfile = os.path.expanduser('~/.lodgeitrc')
elif os.name == 'nt' and 'APPDATA' in os.environ:
rcfile = os.path.expandvars(r'$APPDATA\_lodgeitrc')
if rcfile:
try:
f = open(rcfile)
for line in f:
if line.strip()[:1] in '#;':
continue
p = line.split('=', 1)
if len(p) == 2:
key = p[0].strip().lower()
if key in settings:
if key in ('private', 'clipboard', 'open_browser'):
settings[key] = p[1].strip().lower() in \
('true', '1', 'on', 'yes')
else:
settings[key] = p[1].strip()
f.close()
except IOError:
pass
settings['tags'] = []
settings['title'] = None
return settings
def make_utf8(text, encoding):
"""Convert a text to utf-8"""
try:
u = unicode(text, 'utf-8')
uenc = 'utf-8'
except UnicodeError:
try:
u = unicode(text, encoding)
uenc = 'utf-8'
except UnicodeError:
u = unicode(text, 'iso-8859-15', 'ignore')
uenc = 'iso-8859-15'
try:
import chardet
except ImportError:
return u.encode('utf-8')
d = chardet.detect(text)
if d['encoding'] == uenc:
return u.encode('utf-8')
return unicode(text, d['encoding'], 'ignore').encode('utf-8')
def get_xmlrpc_service():
global _xmlrpc_service
import xmlrpclib
try:
_xmlrpc_service
except NameError:
try:
_xmlrpc_service = xmlrpclib.ServerProxy(SERVICE_URL)
except:
fail('Could not connect to Pastebin', -1)
return _xmlrpc_service
def copy_url(url):
"""Copy the url into the clipboard."""
try:
import win32clipboard
import win32con
except ImportError:
try:
import pygtk
pygtk.require('2.0')
import gtk
import gobject
except ImportError:
return
gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD).set_text(url)
gobject.idle_add(gtk.main_quit)
gtk.main()
else:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(url)
win32clipboard.CloseClipboard()
def open_webbrowser(url):
"""Open a new browser window."""
import webbrowser
webbrowser.open(url)
def get_unix_username():
"""Return the current unix username"""
import getpass
return getpass.getuser()
def language_exists(language):
"""Check if a language alias exists."""
xmlrpc = get_xmlrpc_service()
return bool(xmlrpc.pastes.getNameByAlias(language))
def guess_language(data, filename):
"""Guess the language for a given data."""
xmlrpc = get_xmlrpc_service()
try:
import gnomevfs
except ImportError:
pass
else:
mimes = [gnomevfs.get_mime_type_for_data(data)]
if filename:
mimes.append(gnomevfs.get_mime_type(os.path.abspath(filename)))
for mime in mimes:
alias = xmlrpc.pastes.getAliasForMimetype(mime)
if alias and alias != 'text':
return alias
if filename:
alias = xmlrpc.pastes.getAliasForFilename(filename)
if alias:
return alias
return 'text'
def print_languages():
xmlrpc = get_xmlrpc_service()
languages = xmlrpc.pastes.getLanguages()
languages.sort(lambda a, b: cmp(a[1].lower(), b[1].lower()))
print 'Supported Languages:'
for alias, name in languages:
print ' %-30s%s' % (alias, name)
def download_paste(uid):
xmlrpc = get_xmlrpc_service()
paste = xmlrpc.pastes.getPaste(uid)
if not paste:
fail('Paste "%s" does not exist' % uid, 5)
print paste['code']
def create_paste(code, title, author, language, private, tags):
xmlrpc = get_xmlrpc_service()
rv = xmlrpc.pastes.newPaste(language, code, private, title, author, tags)
if not rv:
fail('Could not commit paste. Something went wrong', 4)
return rv['url']
if __name__ == '__main__':
# parse command line
from optparse import OptionParser
parser = OptionParser()
settings = load_default_settings()
parser.add_option('-v', '--version', action='store_true',
help='Print script version')
parser.add_option('-t', '--title',
help='Title of the paste (default is FILE if given)')
parser.add_option('-a', '--author', default=settings['author'],
help='Name of the author (default is UNIX username)')
parser.add_option('-l', '--language', default=settings['language'],
help='Used syntax highlighter for the file')
parser.add_option('-p', '--private', default=settings['private'],
action='store_true', help='Paste as private')
parser.add_option('-e', '--encoding', default=settings['encoding'],
help='Specify the encoding of a file (default is '
'utf-8 or guessing if available)')
parser.add_option('--no-clipboard', dest='clipboard',
action='store_false',
default=settings['clipboard'],
help="Don't copy the url into the clipboard")
parser.add_option('--open-browser', dest='open_browser',
action='store_true',
default=settings['open_browser'],
help='Open the paste in a web browser')
parser.add_option('--tags', help='List of comma separated tags')
parser.add_option('--languages', action='store_true', default=False,
help='Retrieve a list of supported languages')
parser.add_option('--download', metavar='UID',
help='Download a given paste')
opts, args = parser.parse_args()
if len(args) > 1:
fail('Can only paste one file', 1)
# System Version
if opts.version:
print '%s: version %s' % (SCRIPT_NAME, VERSION)
sys.exit()
# Languages
elif opts.languages:
print_languages()
sys.exit()
# Download Paste
elif opts.download:
download_paste(opts.download)
sys.exit()
if opts.tags:
opts.tags = [t.strip() for t in opts.tags.split(',')]
else:
opts.tags = []
# check language if given
if opts.language and not language_exists(opts.language):
fail('Language %s is not supported', 3)
# load file
try:
if args:
f = file(args[0], 'r')
else:
f = sys.stdin
data = f.read()
f.close()
except Exception, msg:
fail('Error while reading the file: %s' % msg, 2)
if not data.strip():
fail('Aborted, paste file was empty', 4)
# fill with default settings
if not opts.author:
opts.author = get_unix_username()
if not opts.title:
if args:
opts.title = args[0]
else:
opts.title = ''
if not opts.language:
opts.language = guess_language(data, args and args[0] or None)
# create paste
code = make_utf8(data, opts.encoding)
url = create_paste(code, opts.title, opts.author, opts.language,
opts.private, opts.tags)
print url
if opts.open_browser:
open_webbrowser(url)
if opts.clipboard:
copy_url(url)

11
pastebin/manage.py Normal file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

View File

@ -0,0 +1,311 @@
body {
background-color: #919191;
font-family: 'Verdana', 'Bitstream Vera Sans', sans-serif;
font-size: 13px;
margin: 0;
padding: 0;
}
a {
color: #19476D;
}
a:hover {
color: #BA832B;
}
input, select {
border: 1px solid #aaa;
font-family: 'Verdana', 'Bitstream Vera Sans', sans-serif;
font-size: 13px;
}
input:focus {
border: 1px solid #BA832B;
}
textarea {
width: 100%;
height: 300px;
border: 1px solid #aaa;
font-family: 'Bitstream Vera Sans Mono', 'Courier New', monospace;
font-size: 13px;
}
textarea:focus {
border: 1px solid #BA832B;
}
div.action {
margin: 10px 0 0 0;
border-top: 1px dashed #aaa;
padding: 10px;
text-align: center;
}
div.header {
margin: 0;
height: 50px;
border-bottom: 4px solid #2B7ABA;
background-color: #19476D;
}
div.header h1 {
margin: 0;
padding: 0 0 0 10px;
line-height: 50px;
float: left;
}
div.header h1 a {
font-family: 'Helvetica', 'Arial', sans-serif;
font-size: 30px;
color: white;
text-decoration: none;
}
div.header ul {
margin: 0;
padding: 5px 10px 0 0;
float: right;
list-style: none;
}
div.header ul li {
float: left;
font-family: 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
}
div.header ul li a {
display: block;
padding: 5px;
color: white;
text-decoration: none;
font-weight: bold;
}
div.header ul li a:hover {
color: #2B7ABA;
background-color: white;
}
div.contentwrapper {
padding: 10px;
}
div.content {
margin: 0 0 20px 0;
float: left;
width: 75%;
background-color: white;
border-right: 3px solid #777;
border-bottom: 3px solid #777;
}
div.content h2 {
margin: 0 0 10px 0;
padding: 0;
color: #19476D;
font-size: 30px;
}
div.content h3 {
margin: 15px 0 5px 0;
padding: 0;
font-size: 20px;
}
div.content p {
margin: 8px 0 8px 0;
padding: 0;
}
div.content dl dt {
font-weight: bold;
}
div.content dl dd {
margin: 1px 0 3px 35px;
}
div.sidebar {
width: 22%;
float: right;
border-left: 3px solid #222;
border-bottom: 3px solid #222;
background-color: #444;
}
div.sidebar a {
color: #BA832B;
}
div.sidebar a:hover {
color: white;
}
div.sidebar h3 {
margin: 0;
padding: 3px;
background-color: #6E6E6E;
color: #ddd;
font-size: 17px;
font-family: 'Helvetica', 'Arial', sans-serif;
letter-spacing: 2px;
}
div.sidebar form,
div.sidebar ul,
div.sidebar dl,
div.sidebar div.tagcloud {
margin: 5px 0 20px 0;
padding: 0;
}
div.sidebar dl {
color: white;
}
div.sidebar dl dt {
margin: 0;
padding: 0;
font-weight: bold;
color: #2B7ABA;
}
div.sidebar dl dd {
margin: 2px 0 3px 30px;
padding: 0;
}
div.tagcloud a {
text-decoration: none;
padding: 3px;
}
div.sidebar ul {
color: white;
margin-left: 25px;
}
div.sidebar select {
color: white;
background-color: #333;
width: 100%;
}
div.syntax,
div.plain {
padding: 10px;
border: 1px solid #aaa;
overflow: auto;
}
div.syntax pre,
div.plain pre {
margin: 0;
padding: 0;
font-family: 'Bitstream Vera Sans Mono', 'Courier New', monospace;
font-size: 13px;
}
div.syntax td.linenos,
div.plain td.linenos {
padding: 0 20px 0 0;
opacity: 0.5;
}
div.autocomplete {
border: 1px solid #ccc;
background-color: #f2f2f2;
}
div.autocomplete ul {
list-style: none;
margin: 0;
padding: 0;
}
div.autocomplete ul li {
margin: 0;
padding: 4px;
color: #19476D;
}
div.autocomplete ul li.selected {
background-color: #BA832B;
color: white;
font-weight: bold;
}
div.pastethread {
margin: -15px 0 15px 20px;
padding: 5px;
border: 2px solid #777;
background-color: #333;
}
div.pastethread ul {
margin: 0;
padding: 0;
list-style: none;
}
div.pastethread ul li {
margin: 0;
padding: 0;
}
li.paste_summary div.syntax {
padding: 6px;
}
li.paste_summary a.detail {
color: black;
}
li.paste_summary a.detail:hover div.syntax {
border-color: #BA832B;
}
li.paste_summary div.syntax pre {
font-size: 11px;
}
table.diff {
border-collapse: collapse;
border: none;
width: 100%;
border: 1px solid #111;
}
table.diff th {
padding: 5px;
background-color: #444;
color: white;
}
table.diff td {
font-family: 'Bitstream Vera Sans Mono', 'Courier New', sans-serif;
font-size: 9px;
}
table.diff td.diff_next {
background-color: #eee;
padding: 0 3px 0 3px;
}
table.diff td.diff_header {
padding: 2px;
background-color: #555;
color: white;
}
table.diff td.diff_data {
padding: 0 5px 0 5px;
white-space: pre;
}
table.diff .diff_sub {
background-color: #fbc2c2;
}
table.diff .diff_add {
background-color: #9bd29b;
}
table.diff .diff_chg {
background-color: #decba6;
}

View File

@ -0,0 +1,119 @@
// script.aculo.us builder.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// See scriptaculous.js for full license.
var Builder = {
NODEMAP: {
AREA: 'map',
CAPTION: 'table',
COL: 'table',
COLGROUP: 'table',
LEGEND: 'fieldset',
OPTGROUP: 'select',
OPTION: 'select',
PARAM: 'object',
TBODY: 'table',
TD: 'table',
TFOOT: 'table',
TH: 'table',
THEAD: 'table',
TR: 'table'
},
// note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
// due to a Firefox bug
node: function(elementName) {
elementName = elementName.toUpperCase();
// try innerHTML approach
var parentTag = this.NODEMAP[elementName] || 'div';
var parentElement = document.createElement(parentTag);
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
} catch(e) {}
var element = parentElement.firstChild || null;
// see if browser added wrapping tags
if(element && (element.tagName != elementName))
element = element.getElementsByTagName(elementName)[0];
// fallback to createElement approach
if(!element) element = document.createElement(elementName);
// abort if nothing could be created
if(!element) return;
// attributes (or text)
if(arguments[1])
if(this._isStringOrNumber(arguments[1]) ||
(arguments[1] instanceof Array)) {
this._children(element, arguments[1]);
} else {
var attrs = this._attributes(arguments[1]);
if(attrs.length) {
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
parentElement.innerHTML = "<" +elementName + " " +
attrs + "></" + elementName + ">";
} catch(e) {}
element = parentElement.firstChild || null;
// workaround firefox 1.0.X bug
if(!element) {
element = document.createElement(elementName);
for(attr in arguments[1])
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
}
if(element.tagName != elementName)
element = parentElement.getElementsByTagName(elementName)[0];
}
}
// text, or array of children
if(arguments[2])
this._children(element, arguments[2]);
return element;
},
_text: function(text) {
return document.createTextNode(text);
},
_attributes: function(attributes) {
var attrs = [];
for(attribute in attributes)
attrs.push((attribute=='className' ? 'class' : attribute) +
'="' + attributes[attribute].toString().escapeHTML() + '"');
return attrs.join(" ");
},
_children: function(element, children) {
if(typeof children=='object') { // array can hold nodes and text
children.flatten().each( function(e) {
if(typeof e=='object')
element.appendChild(e)
else
if(Builder._isStringOrNumber(e))
element.appendChild(Builder._text(e));
});
} else
if(Builder._isStringOrNumber(children))
element.appendChild(Builder._text(children));
},
_isStringOrNumber: function(param) {
return(typeof param=='string' || typeof param=='number');
},
dump: function(scope) {
if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
tags.each( function(tag){
scope[tag] = function() {
return Builder.node.apply(Builder, [tag].concat($A(arguments)));
}
});
}
}

833
pastebin/media/js/controls.js vendored Normal file
View File

@ -0,0 +1,833 @@
// script.aculo.us controls.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
// See scriptaculous.js for full license.
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library");
var Autocompleter = {}
Autocompleter.Base = function() {};
Autocompleter.Base.prototype = {
baseInitialize: function(element, update, options) {
this.element = $(element);
this.update = $(update);
this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
this.entryCount = 0;
if(this.setOptions)
this.setOptions(options);
else
this.options = options || {};
this.options.paramName = this.options.paramName || this.element.name;
this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
Position.clone(element, update, {
setHeight: false,
offsetTop: element.offsetHeight
});
}
Effect.Appear(update,{duration:0.15});
};
this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
if(typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
this.observer = null;
this.element.setAttribute('autocomplete','off');
Element.hide(this.update);
Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
},
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix &&
(navigator.appVersion.indexOf('MSIE')>0) &&
(navigator.userAgent.indexOf('Opera')<0) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
this.iefix = $(this.update.id+'_iefix');
}
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
},
fixIEOverlapping: function() {
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
this.iefix.style.zIndex = 1;
this.update.style.zIndex = 2;
Element.show(this.iefix);
},
hide: function() {
this.stopIndicator();
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
if(this.iefix) Element.hide(this.iefix);
},
startIndicator: function() {
if(this.options.indicator) Element.show(this.options.indicator);
},
stopIndicator: function() {
if(this.options.indicator) Element.hide(this.options.indicator);
},
onKeyPress: function(event) {
if(this.active)
switch(event.keyCode) {
case Event.KEY_TAB:
case Event.KEY_RETURN:
this.selectEntry();
Event.stop(event);
case Event.KEY_ESC:
this.hide();
this.active = false;
Event.stop(event);
return;
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_UP:
this.markPrevious();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
case Event.KEY_DOWN:
this.markNext();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
}
else
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
(navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
this.changed = true;
this.hasFocus = true;
if(this.observer) clearTimeout(this.observer);
this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
activate: function() {
this.changed = false;
this.hasFocus = true;
this.getUpdatedChoices();
},
onHover: function(event) {
var element = Event.findElement(event, 'LI');
if(this.index != element.autocompleteIndex)
{
this.index = element.autocompleteIndex;
this.render();
}
Event.stop(event);
},
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
this.selectEntry();
this.hide();
},
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
this.hasFocus = false;
this.active = false;
},
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
this.index==i ?
Element.addClassName(this.getEntry(i),"selected") :
Element.removeClassName(this.getEntry(i),"selected");
if(this.hasFocus) {
this.show();
this.active = true;
}
} else {
this.active = false;
this.hide();
}
},
markPrevious: function() {
if(this.index > 0) this.index--
else this.index = this.entryCount-1;
this.getEntry(this.index).scrollIntoView(true);
},
markNext: function() {
if(this.index < this.entryCount-1) this.index++
else this.index = 0;
this.getEntry(this.index).scrollIntoView(false);
},
getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
getCurrentEntry: function() {
return this.getEntry(this.index);
},
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
},
updateElement: function(selectedElement) {
if (this.options.updateElement) {
this.options.updateElement(selectedElement);
return;
}
var value = '';
if (this.options.select) {
var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
} else
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
var lastTokenPos = this.findLastToken();
if (lastTokenPos != -1) {
var newValue = this.element.value.substr(0, lastTokenPos + 1);
var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
if (whitespace)
newValue += whitespace[0];
this.element.value = newValue + value;
} else {
this.element.value = value;
}
this.element.focus();
if (this.options.afterUpdateElement)
this.options.afterUpdateElement(this.element, selectedElement);
},
updateChoices: function(choices) {
if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
Element.cleanWhitespace(this.update.firstChild);
if(this.update.firstChild && this.update.firstChild.childNodes) {
this.entryCount =
this.update.firstChild.childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
if(this.entryCount==1 && this.options.autoSelect) {
this.selectEntry();
this.hide();
} else {
this.render();
}
}
},
addObservers: function(element) {
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
},
onObserverEvent: function() {
this.changed = false;
if(this.getToken().length>=this.options.minChars) {
this.startIndicator();
this.getUpdatedChoices();
} else {
this.active = false;
this.hide();
}
},
getToken: function() {
var tokenPos = this.findLastToken();
if (tokenPos != -1)
var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
else
var ret = this.element.value;
return /\n/.test(ret) ? '' : ret;
},
findLastToken: function() {
var lastTokenPos = -1;
for (var i=0; i<this.options.tokens.length; i++) {
var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
if (thisTokenPos > lastTokenPos)
lastTokenPos = thisTokenPos;
}
return lastTokenPos;
}
}
Ajax.Autocompleter = Class.create();
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
initialize: function(element, update, url, options) {
this.baseInitialize(element, update, options);
this.options.asynchronous = true;
this.options.onComplete = this.onComplete.bind(this);
this.options.defaultParams = this.options.parameters || null;
this.url = url;
},
getUpdatedChoices: function() {
entry = encodeURIComponent(this.options.paramName) + '=' +
encodeURIComponent(this.getToken());
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
new Ajax.Request(this.url, this.options);
},
onComplete: function(request) {
this.updateChoices(request.responseText);
}
});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
// the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
Autocompleter.Local = Class.create();
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
initialize: function(element, update, array, options) {
this.baseInitialize(element, update, options);
this.options.array = array;
},
getUpdatedChoices: function() {
this.updateChoices(this.options.selector(this));
},
setOptions: function(options) {
this.options = Object.extend({
choices: 10,
partialSearch: true,
partialChars: 2,
ignoreCase: true,
fullSearch: false,
selector: function(instance) {
var ret = []; // Beginning matches
var partial = []; // Inside matches
var entry = instance.getToken();
var count = 0;
for (var i = 0; i < instance.options.array.length &&
ret.length < instance.options.choices ; i++) {
var elem = instance.options.array[i];
var foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
while (foundPos != -1) {
if (foundPos == 0 && elem.length != entry.length) {
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
} else if (entry.length >= instance.options.partialChars &&
instance.options.partialSearch && foundPos != -1) {
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
foundPos + entry.length) + "</li>");
break;
}
}
foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
elem.indexOf(entry, foundPos + 1);
}
}
if (partial.length)
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
return "<ul>" + ret.join('') + "</ul>";
}
}, options || {});
}
});
// AJAX in-place editor
//
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
setTimeout(function() {
Field.activate(field);
}, 1);
}
Ajax.InPlaceEditor = Class.create();
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
Ajax.InPlaceEditor.prototype = {
initialize: function(element, url, options) {
this.url = url;
this.element = $(element);
this.options = Object.extend({
okButton: true,
okText: "ok",
cancelLink: true,
cancelText: "cancel",
savingText: "Saving...",
clickToEditText: "Click to edit",
okText: "ok",
rows: 1,
onComplete: function(transport, element) {
new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
},
onFailure: function(transport) {
alert("Error communicating with the server: " + transport.responseText.stripTags());
},
callback: function(form) {
return Form.serialize(form);
},
handleLineBreaks: true,
loadingText: 'Loading...',
savingClassName: 'inplaceeditor-saving',
loadingClassName: 'inplaceeditor-loading',
formClassName: 'inplaceeditor-form',
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
highlightendcolor: "#FFFFFF",
externalControl: null,
submitOnBlur: false,
ajaxOptions: {},
evalScripts: false
}, options || {});
if(!this.options.formId && this.element.id) {
this.options.formId = this.element.id + "-inplaceeditor";
if ($(this.options.formId)) {
// there's already a form with that name, don't specify an id
this.options.formId = null;
}
}
if (this.options.externalControl) {
this.options.externalControl = $(this.options.externalControl);
}
this.originalBackground = Element.getStyle(this.element, 'background-color');
if (!this.originalBackground) {
this.originalBackground = "transparent";
}
this.element.title = this.options.clickToEditText;
this.onclickListener = this.enterEditMode.bindAsEventListener(this);
this.mouseoverListener = this.enterHover.bindAsEventListener(this);
this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
Event.observe(this.element, 'click', this.onclickListener);
Event.observe(this.element, 'mouseover', this.mouseoverListener);
Event.observe(this.element, 'mouseout', this.mouseoutListener);
if (this.options.externalControl) {
Event.observe(this.options.externalControl, 'click', this.onclickListener);
Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
}
},
enterEditMode: function(evt) {
if (this.saving) return;
if (this.editing) return;
this.editing = true;
this.onEnterEditMode();
if (this.options.externalControl) {
Element.hide(this.options.externalControl);
}
Element.hide(this.element);
this.createForm();
this.element.parentNode.insertBefore(this.form, this.element);
if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
// stop the event to avoid a page refresh in Safari
if (evt) {
Event.stop(evt);
}
return false;
},
createForm: function() {
this.form = document.createElement("form");
this.form.id = this.options.formId;
Element.addClassName(this.form, this.options.formClassName)
this.form.onsubmit = this.onSubmit.bind(this);
this.createEditField();
if (this.options.textarea) {
var br = document.createElement("br");
this.form.appendChild(br);
}
if (this.options.okButton) {
okButton = document.createElement("input");
okButton.type = "submit";
okButton.value = this.options.okText;
okButton.className = 'editor_ok_button';
this.form.appendChild(okButton);
}
if (this.options.cancelLink) {
cancelLink = document.createElement("a");
cancelLink.href = "#";
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
cancelLink.onclick = this.onclickCancel.bind(this);
cancelLink.className = 'editor_cancel';
this.form.appendChild(cancelLink);
}
},
hasHTMLLineBreaks: function(string) {
if (!this.options.handleLineBreaks) return false;
return string.match(/<br/i) || string.match(/<p>/i);
},
convertHTMLLineBreaks: function(string) {
return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
},
createEditField: function() {
var text;
if(this.options.loadTextURL) {
text = this.options.loadingText;
} else {
text = this.getText();
}
var obj = this;
if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
this.options.textarea = false;
var textField = document.createElement("input");
textField.obj = this;
textField.type = "text";
textField.name = "value";
textField.value = text;
textField.style.backgroundColor = this.options.highlightcolor;
textField.className = 'editor_field';
var size = this.options.size || this.options.cols || 0;
if (size != 0) textField.size = size;
if (this.options.submitOnBlur)
textField.onblur = this.onSubmit.bind(this);
this.editField = textField;
} else {
this.options.textarea = true;
var textArea = document.createElement("textarea");
textArea.obj = this;
textArea.name = "value";
textArea.value = this.convertHTMLLineBreaks(text);
textArea.rows = this.options.rows;
textArea.cols = this.options.cols || 40;
textArea.className = 'editor_field';
if (this.options.submitOnBlur)
textArea.onblur = this.onSubmit.bind(this);
this.editField = textArea;
}
if(this.options.loadTextURL) {
this.loadExternalText();
}
this.form.appendChild(this.editField);
},
getText: function() {
return this.element.innerHTML;
},
loadExternalText: function() {
Element.addClassName(this.form, this.options.loadingClassName);
this.editField.disabled = true;
new Ajax.Request(
this.options.loadTextURL,
Object.extend({
asynchronous: true,
onComplete: this.onLoadedExternalText.bind(this)
}, this.options.ajaxOptions)
);
},
onLoadedExternalText: function(transport) {
Element.removeClassName(this.form, this.options.loadingClassName);
this.editField.disabled = false;
this.editField.value = transport.responseText.stripTags();
Field.scrollFreeActivate(this.editField);
},
onclickCancel: function() {
this.onComplete();
this.leaveEditMode();
return false;
},
onFailure: function(transport) {
this.options.onFailure(transport);
if (this.oldInnerHTML) {
this.element.innerHTML = this.oldInnerHTML;
this.oldInnerHTML = null;
}
return false;
},
onSubmit: function() {
// onLoading resets these so we need to save them away for the Ajax call
var form = this.form;
var value = this.editField.value;
// do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
// which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
// to be displayed indefinitely
this.onLoading();
if (this.options.evalScripts) {
new Ajax.Request(
this.url, Object.extend({
parameters: this.options.callback(form, value),
onComplete: this.onComplete.bind(this),
onFailure: this.onFailure.bind(this),
asynchronous:true,
evalScripts:true
}, this.options.ajaxOptions));
} else {
new Ajax.Updater(
{ success: this.element,
// don't update on failure (this could be an option)
failure: null },
this.url, Object.extend({
parameters: this.options.callback(form, value),
onComplete: this.onComplete.bind(this),
onFailure: this.onFailure.bind(this)
}, this.options.ajaxOptions));
}
// stop the event to avoid a page refresh in Safari
if (arguments.length > 1) {
Event.stop(arguments[0]);
}
return false;
},
onLoading: function() {
this.saving = true;
this.removeForm();
this.leaveHover();
this.showSaving();
},
showSaving: function() {
this.oldInnerHTML = this.element.innerHTML;
this.element.innerHTML = this.options.savingText;
Element.addClassName(this.element, this.options.savingClassName);
this.element.style.backgroundColor = this.originalBackground;
Element.show(this.element);
},
removeForm: function() {
if(this.form) {
if (this.form.parentNode) Element.remove(this.form);
this.form = null;
}
},
enterHover: function() {
if (this.saving) return;
this.element.style.backgroundColor = this.options.highlightcolor;
if (this.effect) {
this.effect.cancel();
}
Element.addClassName(this.element, this.options.hoverClassName)
},
leaveHover: function() {
if (this.options.backgroundColor) {
this.element.style.backgroundColor = this.oldBackground;
}
Element.removeClassName(this.element, this.options.hoverClassName)
if (this.saving) return;
this.effect = new Effect.Highlight(this.element, {
startcolor: this.options.highlightcolor,
endcolor: this.options.highlightendcolor,
restorecolor: this.originalBackground
});
},
leaveEditMode: function() {
Element.removeClassName(this.element, this.options.savingClassName);
this.removeForm();
this.leaveHover();
this.element.style.backgroundColor = this.originalBackground;
Element.show(this.element);
if (this.options.externalControl) {
Element.show(this.options.externalControl);
}
this.editing = false;
this.saving = false;
this.oldInnerHTML = null;
this.onLeaveEditMode();
},
onComplete: function(transport) {
this.leaveEditMode();
this.options.onComplete.bind(this)(transport, this.element);
},
onEnterEditMode: function() {},
onLeaveEditMode: function() {},
dispose: function() {
if (this.oldInnerHTML) {
this.element.innerHTML = this.oldInnerHTML;
}
this.leaveEditMode();
Event.stopObserving(this.element, 'click', this.onclickListener);
Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
if (this.options.externalControl) {
Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
}
}
};
Ajax.InPlaceCollectionEditor = Class.create();
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
createEditField: function() {
if (!this.cached_selectTag) {
var selectTag = document.createElement("select");
var collection = this.options.collection || [];
var optionTag;
collection.each(function(e,i) {
optionTag = document.createElement("option");
optionTag.value = (e instanceof Array) ? e[0] : e;
if((typeof this.options.value == 'undefined') &&
((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
if(this.options.value==optionTag.value) optionTag.selected = true;
optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
selectTag.appendChild(optionTag);
}.bind(this));
this.cached_selectTag = selectTag;
}
this.editField = this.cached_selectTag;
if(this.options.loadTextURL) this.loadExternalText();
this.form.appendChild(this.editField);
this.options.callback = function(form, value) {
return "value=" + encodeURIComponent(value);
}
}
});
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
this.callback = callback;
this.timer = null;
this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
if(this.lastValue == $F(this.element)) return;
if(this.timer) clearTimeout(this.timer);
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
this.lastValue = $F(this.element);
},
onTimerEvent: function() {
this.timer = null;
this.callback(this.element, $F(this.element));
}
};

970
pastebin/media/js/dragdrop.js vendored Normal file
View File

@ -0,0 +1,970 @@
// script.aculo.us dragdrop.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// See scriptaculous.js for full license.
/*--------------------------------------------------------------------------*/
if(typeof Effect == 'undefined')
throw("dragdrop.js requires including script.aculo.us' effects.js library");
var Droppables = {
drops: [],
remove: function(element) {
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
},
add: function(element) {
element = $(element);
var options = Object.extend({
greedy: true,
hoverclass: null,
tree: false
}, arguments[1] || {});
// cache containers
if(options.containment) {
options._containers = [];
var containment = options.containment;
if((typeof containment == 'object') &&
(containment.constructor == Array)) {
containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
}
}
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
options.element = element;
this.drops.push(options);
},
findDeepestChild: function(drops) {
deepest = drops[0];
for (i = 1; i < drops.length; ++i)
if (Element.isParent(drops[i].element, deepest.element))
deepest = drops[i];
return deepest;
},
isContained: function(element, drop) {
var containmentNode;
if(drop.tree) {
containmentNode = element.treeNode;
} else {
containmentNode = element.parentNode;
}
return drop._containers.detect(function(c) { return containmentNode == c });
},
isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
deactivate: function(drop) {
if(drop.hoverclass)
Element.removeClassName(drop.element, drop.hoverclass);
this.last_active = null;
},
activate: function(drop) {
if(drop.hoverclass)
Element.addClassName(drop.element, drop.hoverclass);
this.last_active = drop;
},
show: function(point, element) {
if(!this.drops.length) return;
var affected = [];
if(this.last_active) this.deactivate(this.last_active);
this.drops.each( function(drop) {
if(Droppables.isAffected(point, element, drop))
affected.push(drop);
});
if(affected.length>0) {
drop = Droppables.findDeepestChild(affected);
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
Droppables.activate(drop);
}
},
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop)
this.last_active.onDrop(element, this.last_active.element, event);
},
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
}
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
if(draggable.options.delay) {
this._timeout = setTimeout(function() {
Draggables._timeout = null;
window.focus();
Draggables.activeDraggable = draggable;
}.bind(this), draggable.options.delay);
} else {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
}
},
deactivate: function() {
this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// Mozilla-based browsers fire successive mousemove events with
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
if(this._timeout) {
clearTimeout(this._timeout);
this._timeout = null;
}
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
this.activeDraggable = null;
},
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
},
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
}
/*--------------------------------------------------------------------------*/
var Draggable = Class.create();
Draggable._dragging = {};
Draggable.prototype = {
initialize: function(element) {
var defaults = {
handle: false,
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
queue: {scope:'_draggable', position:'end'}
});
},
endeffect: function(element) {
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
afterFinish: function(){
Draggable._dragging[element] = false
}
});
},
zindex: 1000,
revert: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
delay: 0
};
if(arguments[1] && typeof arguments[1].endeffect == 'undefined')
Object.extend(defaults, {
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
Draggable._dragging[element] = true;
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
}
});
var options = Object.extend(defaults, arguments[1] || {});
this.element = $(element);
if(options.handle && (typeof options.handle == 'string')) {
var h = Element.childrenWithClassName(this.element, options.handle, true);
if(h.length>0) this.handle = h[0];
}
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
options.scroll = $(options.scroll);
this._isScrollChild = Element.childOf(this.element, options.scroll);
}
Element.makePositioned(this.element); // fix IE
this.delta = this.currentDelta();
this.options = options;
this.dragging = false;
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
Draggables.register(this);
},
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
Draggables.unregister(this);
},
currentDelta: function() {
return([
parseInt(Element.getStyle(this.element,'left') || '0'),
parseInt(Element.getStyle(this.element,'top') || '0')]);
},
initDrag: function(event) {
if(typeof Draggable._dragging[this.element] != 'undefined' &&
Draggable._dragging[this.element]) return;
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if(src.tagName && (
src.tagName=='INPUT' ||
src.tagName=='SELECT' ||
src.tagName=='OPTION' ||
src.tagName=='BUTTON' ||
src.tagName=='TEXTAREA')) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function(event) {
this.dragging = true;
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
if(this.options.scroll) {
if (this.options.scroll == window) {
var where = this._getWindowScroll(this.options.scroll);
this.originalScrollLeft = where.left;
this.originalScrollTop = where.top;
} else {
this.originalScrollLeft = this.options.scroll.scrollLeft;
this.originalScrollTop = this.options.scroll.scrollTop;
}
}
Draggables.notify('onStart', this, event);
if(this.options.starteffect) this.options.starteffect(this.element);
},
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
Position.prepare();
Droppables.show(pointer, this.element);
Draggables.notify('onDrag', this, event);
this.draw(pointer);
if(this.options.change) this.options.change(this);
if(this.options.scroll) {
this.stopScrolling();
var p;
if (this.options.scroll == window) {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
} else {
p = Position.page(this.options.scroll);
p[0] += this.options.scroll.scrollLeft;
p[1] += this.options.scroll.scrollTop;
p[0] += (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
p[1] += (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
p.push(p[0]+this.options.scroll.offsetWidth);
p.push(p[1]+this.options.scroll.offsetHeight);
}
var speed = [0,0];
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
this.startScrolling(speed);
}
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
},
finishDrag: function(event, success) {
this.dragging = false;
if(this.options.ghosting) {
Position.relativize(this.element);
Element.remove(this._clone);
this._clone = null;
}
if(success) Droppables.fire(event, this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && typeof revert == 'function') revert = revert(this.element);
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
this.options.reverteffect(this.element,
d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.delta = d;
}
if(this.options.zindex)
this.element.style.zIndex = this.originalZ;
if(this.options.endeffect)
this.options.endeffect(this.element);
Draggables.deactivate(this);
Droppables.reset();
},
keyPress: function(event) {
if(event.keyCode!=Event.KEY_ESC) return;
this.finishDrag(event, false);
Event.stop(event);
},
endDrag: function(event) {
if(!this.dragging) return;
this.stopScrolling();
this.finishDrag(event, true);
Event.stop(event);
},
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
if(this.options.ghosting) {
var r = Position.realOffset(this.element);
window.status = r.inspect();
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
}
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
var p = [0,1].map(function(i){
return (point[i]-pos[i]-this.offset[i])
}.bind(this));
if(this.options.snap) {
if(typeof this.options.snap == 'function') {
p = this.options.snap(p[0],p[1],this);
} else {
if(this.options.snap instanceof Array) {
p = p.map( function(v, i) {
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
} else {
p = p.map( function(v) {
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
}
}}
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
stopScrolling: function() {
if(this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
Draggables._lastScrollPointer = null;
}
},
startScrolling: function(speed) {
if(!(speed[0] || speed[1])) return;
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
this.lastScrolled = new Date();
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
},
scroll: function() {
var current = new Date();
var delta = current - this.lastScrolled;
this.lastScrolled = current;
if(this.options.scroll == window) {
with (this._getWindowScroll(this.options.scroll)) {
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
var d = delta / 1000;
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
}
}
} else {
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
}
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
if (this._isScrollChild) {
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
if (Draggables._lastScrollPointer[0] < 0)
Draggables._lastScrollPointer[0] = 0;
if (Draggables._lastScrollPointer[1] < 0)
Draggables._lastScrollPointer[1] = 0;
this.draw(Draggables._lastScrollPointer);
}
if(this.options.change) this.options.change(this);
},
_getWindowScroll: function(w) {
var T, L, W, H;
with (w.document) {
if (w.document.documentElement && documentElement.scrollTop) {
T = documentElement.scrollTop;
L = documentElement.scrollLeft;
} else if (w.document.body) {
T = body.scrollTop;
L = body.scrollLeft;
}
if (w.innerWidth) {
W = w.innerWidth;
H = w.innerHeight;
} else if (w.document.documentElement && documentElement.clientWidth) {
W = documentElement.clientWidth;
H = documentElement.clientHeight;
} else {
W = body.offsetWidth;
H = body.offsetHeight
}
}
return { top: T, left: L, width: W, height: H };
}
}
/*--------------------------------------------------------------------------*/
var SortableObserver = Class.create();
SortableObserver.prototype = {
initialize: function(element, observer) {
this.element = $(element);
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
}
var Sortable = {
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
sortables: {},
_findRootElement: function(element) {
while (element.tagName != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
},
options: function(element) {
element = Sortable._findRootElement($(element));
if(!element) return;
return Sortable.sortables[element.id];
},
destroy: function(element){
var s = Sortable.options(element);
if(s) {
Draggables.removeObserver(s.element);
s.droppables.each(function(d){ Droppables.remove(d) });
s.draggables.invoke('destroy');
delete Sortable.sortables[s.element.id];
}
},
create: function(element) {
element = $(element);
var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
dropOnEmpty: false,
tree: false,
treeTag: 'ul',
overlap: 'vertical', // one of 'vertical', 'horizontal'
constraint: 'vertical', // one of 'vertical', 'horizontal', false
containment: element, // also takes array of elements (or id's); or false
handle: false, // or a CSS class
only: false,
delay: 0,
hoverclass: null,
ghosting: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: this.SERIALIZE_RULE,
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || {});
// clear any old sortable with same element
this.destroy(element);
// build options for the draggables
var options_for_draggable = {
revert: true,
scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
delay: options.delay,
ghosting: options.ghosting,
constraint: options.constraint,
handle: options.handle };
if(options.starteffect)
options_for_draggable.starteffect = options.starteffect;
if(options.reverteffect)
options_for_draggable.reverteffect = options.reverteffect;
else
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
element.style.top = 0;
element.style.left = 0;
};
if(options.endeffect)
options_for_draggable.endeffect = options.endeffect;
if(options.zindex)
options_for_draggable.zindex = options.zindex;
// build options for the droppables
var options_for_droppable = {
overlap: options.overlap,
containment: options.containment,
tree: options.tree,
hoverclass: options.hoverclass,
onHover: Sortable.onHover
//greedy: !options.dropOnEmpty
}
var options_for_tree = {
onHover: Sortable.onEmptyHover,
overlap: options.overlap,
containment: options.containment,
hoverclass: options.hoverclass
}
// fix for gecko engine
Element.cleanWhitespace(element);
options.draggables = [];
options.droppables = [];
// drop on empty handling
if(options.dropOnEmpty || options.tree) {
Droppables.add(element, options_for_tree);
options.droppables.push(element);
}
(this.findElements(element, options) || []).each( function(e) {
// handles are per-draggable
var handle = options.handle ?
Element.childrenWithClassName(e, options.handle)[0] : e;
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
if(options.tree) e.treeNode = element;
options.droppables.push(e);
});
if(options.tree) {
(Sortable.findTreeElements(element, options) || []).each( function(e) {
Droppables.add(e, options_for_tree);
e.treeNode = element;
options.droppables.push(e);
});
}
// keep reference
this.sortables[element.id] = options;
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
},
// return all suitable-for-sortable elements in a guaranteed order
findElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.tag);
},
findTreeElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.treeTag);
},
onHover: function(element, dropon, overlap) {
if(Element.isParent(dropon, element)) return;
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
return;
} else if(overlap>0.5) {
Sortable.mark(dropon, 'before');
if(dropon.previousSibling != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, dropon);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
} else {
Sortable.mark(dropon, 'after');
var nextElement = dropon.nextSibling || null;
if(nextElement != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, nextElement);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
}
},
onEmptyHover: function(element, dropon, overlap) {
var oldParentNode = element.parentNode;
var droponOptions = Sortable.options(dropon);
if(!Element.isParent(dropon, element)) {
var index;
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
var child = null;
if(children) {
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
for (index = 0; index < children.length; index += 1) {
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
offset -= Element.offsetSize (children[index], droponOptions.overlap);
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
child = index + 1 < children.length ? children[index + 1] : null;
break;
} else {
child = children[index];
break;
}
}
}
dropon.insertBefore(element, child);
Sortable.options(oldParentNode).onChange(element);
droponOptions.onChange(element);
}
},
unmark: function() {
if(Sortable._marker) Element.hide(Sortable._marker);
},
mark: function(dropon, position) {
// mark on ghosting only
var sortable = Sortable.options(dropon.parentNode);
if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
Sortable._marker = $('dropmarker') || document.createElement('DIV');
Element.hide(Sortable._marker);
Element.addClassName(Sortable._marker, 'dropmarker');
Sortable._marker.style.position = 'absolute';
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
Sortable._marker.style.left = offsets[0] + 'px';
Sortable._marker.style.top = offsets[1] + 'px';
if(position=='after')
if(sortable.overlap == 'horizontal')
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
else
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
Element.show(Sortable._marker);
},
_tree: function(element, options, parent) {
var children = Sortable.findElements(element, options) || [];
for (var i = 0; i < children.length; ++i) {
var match = children[i].id.match(options.format);
if (!match) continue;
var child = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
parent: parent,
children: new Array,
position: parent.children.length,
container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
}
/* Get the element containing the children and recurse over it */
if (child.container)
this._tree(child.container, options, child)
parent.children.push (child);
}
return parent;
},
/* Finds the first element of the given tag type within a parent element.
Used for finding the first LI[ST] within a L[IST]I[TEM].*/
_findChildrenElement: function (element, containerTag) {
if (element && element.hasChildNodes)
for (var i = 0; i < element.childNodes.length; ++i)
if (element.childNodes[i].tagName == containerTag)
return element.childNodes[i];
return null;
},
tree: function(element) {
element = $(element);
var sortableOptions = this.options(element);
var options = Object.extend({
tag: sortableOptions.tag,
treeTag: sortableOptions.treeTag,
only: sortableOptions.only,
name: element.id,
format: sortableOptions.format
}, arguments[1] || {});
var root = {
id: null,
parent: null,
children: new Array,
container: element,
position: 0
}
return Sortable._tree (element, options, root);
},
/* Construct a [i] index for a particular node */
_constructIndex: function(node) {
var index = '';
do {
if (node.id) index = '[' + node.position + ']' + index;
} while ((node = node.parent) != null);
return index;
},
sequence: function(element) {
element = $(element);
var options = Object.extend(this.options(element), arguments[1] || {});
return $(this.findElements(element, options) || []).map( function(item) {
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
});
},
setSequence: function(element, new_sequence) {
element = $(element);
var options = Object.extend(this.options(element), arguments[2] || {});
var nodeMap = {};
this.findElements(element, options).each( function(n) {
if (n.id.match(options.format))
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
n.parentNode.removeChild(n);
});
new_sequence.each(function(ident) {
var n = nodeMap[ident];
if (n) {
n[1].appendChild(n[0]);
delete nodeMap[ident];
}
});
},
serialize: function(element) {
element = $(element);
var options = Object.extend(Sortable.options(element), arguments[1] || {});
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
if (options.tree) {
return Sortable.tree(element, arguments[1]).children.map( function (item) {
return [name + Sortable._constructIndex(item) + "[id]=" +
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
}).flatten().join('&');
} else {
return Sortable.sequence(element, arguments[1]).map( function(item) {
return name + "[]=" + encodeURIComponent(item);
}).join('&');
}
}
}
/* Returns true if child is contained within element */
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element);
}
Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
var elements = [];
$A(element.childNodes).each( function(e) {
if(e.tagName && e.tagName.toUpperCase()==tagName &&
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
elements.push(e);
if(recursive) {
var grandchildren = Element.findChildren(e, only, recursive, tagName);
if(grandchildren) elements.push(grandchildren);
}
});
return (elements.length>0 ? elements.flatten() : []);
}
Element.offsetSize = function (element, type) {
if (type == 'vertical' || type == 'height')
return element.offsetHeight;
else
return element.offsetWidth;
}

977
pastebin/media/js/effects.js vendored Normal file
View File

@ -0,0 +1,977 @@
// script.aculo.us effects.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
// Martin Bialasinki
//
// See scriptaculous.js for full license.
// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
var color = '#';
if(this.slice(0,4) == 'rgb(') {
var cols = this.slice(4,this.length-1).split(',');
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
} else {
if(this.slice(0,1) == '#') {
if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
if(this.length==7) color = this.toLowerCase();
}
}
return(color.length==7 ? color : (arguments[0] || this));
}
/*--------------------------------------------------------------------------*/
Element.collectTextNodes = function(element) {
return $A($(element).childNodes).collect( function(node) {
return (node.nodeType==3 ? node.nodeValue :
(node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
}).flatten().join('');
}
Element.collectTextNodesIgnoreClass = function(element, className) {
return $A($(element).childNodes).collect( function(node) {
return (node.nodeType==3 ? node.nodeValue :
((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
Element.collectTextNodesIgnoreClass(node, className) : ''));
}).flatten().join('');
}
Element.setContentZoom = function(element, percent) {
element = $(element);
Element.setStyle(element, {fontSize: (percent/100) + 'em'});
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
}
Element.getOpacity = function(element){
var opacity;
if (opacity = Element.getStyle(element, 'opacity'))
return parseFloat(opacity);
if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
if(opacity[1]) return parseFloat(opacity[1]) / 100;
return 1.0;
}
Element.setOpacity = function(element, value){
element= $(element);
if (value == 1){
Element.setStyle(element, { opacity:
(/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : 1.0 });
if(/MSIE/.test(navigator.userAgent) && !window.opera)
Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
Element.setStyle(element, {opacity: value});
if(/MSIE/.test(navigator.userAgent) && !window.opera)
Element.setStyle(element,
{ filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
}
Element.getInlineOpacity = function(element){
return $(element).style.opacity || '';
}
Element.childrenWithClassName = function(element, className, findFirst) {
var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
return (c.className && c.className.match(classNameRegExp));
});
if(!results) results = [];
return results;
}
Element.forceRerendering = function(element) {
try {
element = $(element);
var n = document.createTextNode(' ');
element.appendChild(n);
element.removeChild(n);
} catch(e) { }
};
/*--------------------------------------------------------------------------*/
Array.prototype.call = function() {
var args = arguments;
this.each(function(f){ f.apply(this, args) });
}
/*--------------------------------------------------------------------------*/
var Effect = {
_elementDoesNotExistError: {
name: 'ElementDoesNotExistError',
message: 'The specified DOM element does not exist, but is required for this effect to operate'
},
tagifyText: function(element) {
if(typeof Builder == 'undefined')
throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
var tagifyStyle = 'position:relative';
if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
Builder.node('span',{style: tagifyStyle},
character == ' ' ? String.fromCharCode(160) : character),
child);
});
Element.remove(child);
}
});
},
multiple: function(element, effect) {
var elements;
if(((typeof element == 'object') ||
(typeof element == 'function')) &&
(element.length))
elements = element;
else
elements = $(element).childNodes;
var options = Object.extend({
speed: 0.1,
delay: 0.0
}, arguments[2] || {});
var masterDelay = options.delay;
$A(elements).each( function(element, index) {
new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
});
},
PAIRS: {
'slide': ['SlideDown','SlideUp'],
'blind': ['BlindDown','BlindUp'],
'appear': ['Appear','Fade']
},
toggle: function(element, effect) {
element = $(element);
effect = (effect || 'appear').toLowerCase();
var options = Object.extend({
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
}, arguments[2] || {});
Effect[element.visible() ?
Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
}
};
var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
Effect.Transitions = {}
Effect.Transitions.linear = Prototype.K;
Effect.Transitions.sinoidal = function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
Effect.Transitions.reverse = function(pos) {
return 1-pos;
}
Effect.Transitions.flicker = function(pos) {
return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
}
Effect.Transitions.wobble = function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
}
Effect.Transitions.pulse = function(pos) {
return (Math.floor(pos*10) % 2 == 0 ?
(pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
}
Effect.Transitions.none = function(pos) {
return 0;
}
Effect.Transitions.full = function(pos) {
return 1;
}
/* ------------- core effects ------------- */
Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
initialize: function() {
this.effects = [];
this.interval = null;
},
_each: function(iterator) {
this.effects._each(iterator);
},
add: function(effect) {
var timestamp = new Date().getTime();
var position = (typeof effect.options.queue == 'string') ?
effect.options.queue : effect.options.queue.position;
switch(position) {
case 'front':
// move unstarted effects after this effect
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
e.startOn += effect.finishOn;
e.finishOn += effect.finishOn;
});
break;
case 'end':
// start effect after last queued effect has finished
timestamp = this.effects.pluck('finishOn').max() || timestamp;
break;
}
effect.startOn += timestamp;
effect.finishOn += timestamp;
if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
this.effects.push(effect);
if(!this.interval)
this.interval = setInterval(this.loop.bind(this), 40);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
if(this.effects.length == 0) {
clearInterval(this.interval);
this.interval = null;
}
},
loop: function() {
var timePos = new Date().getTime();
this.effects.invoke('loop', timePos);
}
});
Effect.Queues = {
instances: $H(),
get: function(queueName) {
if(typeof queueName != 'string') return queueName;
if(!this.instances[queueName])
this.instances[queueName] = new Effect.ScopedQueue();
return this.instances[queueName];
}
}
Effect.Queue = Effect.Queues.get('global');
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration: 1.0, // seconds
fps: 25.0, // max. 25fps due to Effect.Queue implementation
sync: false, // true for combining
from: 0.0,
to: 1.0,
delay: 0.0,
queue: 'parallel'
}
Effect.Base = function() {};
Effect.Base.prototype = {
position: null,
start: function(options) {
this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
this.currentFrame = 0;
this.state = 'idle';
this.startOn = this.options.delay*1000;
this.finishOn = this.startOn + (this.options.duration*1000);
this.event('beforeStart');
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'global' : this.options.queue.scope).add(this);
},
loop: function(timePos) {
if(timePos >= this.startOn) {
if(timePos >= this.finishOn) {
this.render(1.0);
this.cancel();
this.event('beforeFinish');
if(this.finish) this.finish();
this.event('afterFinish');
return;
}
var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
var frame = Math.round(pos * this.options.fps * this.options.duration);
if(frame > this.currentFrame) {
this.render(pos);
this.currentFrame = frame;
}
}
},
render: function(pos) {
if(this.state == 'idle') {
this.state = 'running';
this.event('beforeSetup');
if(this.setup) this.setup();
this.event('afterSetup');
}
if(this.state == 'running') {
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
pos += this.options.from;
this.position = pos;
this.event('beforeUpdate');
if(this.update) this.update(pos);
this.event('afterUpdate');
}
},
cancel: function() {
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'global' : this.options.queue.scope).remove(this);
this.state = 'finished';
},
event: function(eventName) {
if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
if(this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
initialize: function(effects) {
this.effects = effects || [];
this.start(arguments[1]);
},
update: function(position) {
this.effects.invoke('render', position);
},
finish: function(position) {
this.effects.each( function(effect) {
effect.render(1.0);
effect.cancel();
effect.event('beforeFinish');
if(effect.finish) effect.finish(position);
effect.event('afterFinish');
});
}
});
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
// make this work on IE on elements without 'layout'
if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
this.element.setStyle({zoom: 1});
var options = Object.extend({
from: this.element.getOpacity() || 0.0,
to: 1.0
}, arguments[1] || {});
this.start(options);
},
update: function(position) {
this.element.setOpacity(position);
}
});
Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
x: 0,
y: 0,
mode: 'relative'
}, arguments[1] || {});
this.start(options);
},
setup: function() {
// Bug in Opera: Opera returns the "real" position of a static element or
// relative element that does not have top/left explicitly set.
// ==> Always set top and left for position relative elements in your stylesheets
// (to 0 if you do not need them)
this.element.makePositioned();
this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
this.originalTop = parseFloat(this.element.getStyle('top') || '0');
if(this.options.mode == 'absolute') {
// absolute movement, so we need to calc deltaX and deltaY
this.options.x = this.options.x - this.originalLeft;
this.options.y = this.options.y - this.originalTop;
}
},
update: function(position) {
this.element.setStyle({
left: Math.round(this.options.x * position + this.originalLeft) + 'px',
top: Math.round(this.options.y * position + this.originalTop) + 'px'
});
}
});
// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
return new Effect.Move(element,
Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};
Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
initialize: function(element, percent) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
scaleX: true,
scaleY: true,
scaleContent: true,
scaleFromCenter: false,
scaleMode: 'box', // 'box' or 'contents' or {} with provided values
scaleFrom: 100.0,
scaleTo: percent
}, arguments[2] || {});
this.start(options);
},
setup: function() {
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
this.elementPositioning = this.element.getStyle('position');
this.originalStyle = {};
['top','left','width','height','fontSize'].each( function(k) {
this.originalStyle[k] = this.element.style[k];
}.bind(this));
this.originalTop = this.element.offsetTop;
this.originalLeft = this.element.offsetLeft;
var fontSize = this.element.getStyle('font-size') || '100%';
['em','px','%','pt'].each( function(fontSizeType) {
if(fontSize.indexOf(fontSizeType)>0) {
this.fontSize = parseFloat(fontSize);
this.fontSizeType = fontSizeType;
}
}.bind(this));
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
this.dims = null;
if(this.options.scaleMode=='box')
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
if(/^content/.test(this.options.scaleMode))
this.dims = [this.element.scrollHeight, this.element.scrollWidth];
if(!this.dims)
this.dims = [this.options.scaleMode.originalHeight,
this.options.scaleMode.originalWidth];
},
update: function(position) {
var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
if(this.options.scaleContent && this.fontSize)
this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
},
finish: function(position) {
if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
},
setDimensions: function(height, width) {
var d = {};
if(this.options.scaleX) d.width = Math.round(width) + 'px';
if(this.options.scaleY) d.height = Math.round(height) + 'px';
if(this.options.scaleFromCenter) {
var topd = (height - this.dims[0])/2;
var leftd = (width - this.dims[1])/2;
if(this.elementPositioning == 'absolute') {
if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
} else {
if(this.options.scaleY) d.top = -topd + 'px';
if(this.options.scaleX) d.left = -leftd + 'px';
}
}
this.element.setStyle(d);
}
});
Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
this.start(options);
},
setup: function() {
// Prevent executing on elements not in the layout flow
if(this.element.getStyle('display')=='none') { this.cancel(); return; }
// Disable background image during the effect
this.oldStyle = {
backgroundImage: this.element.getStyle('background-image') };
this.element.setStyle({backgroundImage: 'none'});
if(!this.options.endcolor)
this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
if(!this.options.restorecolor)
this.options.restorecolor = this.element.getStyle('background-color');
// init color calculations
this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
},
update: function(position) {
this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
},
finish: function() {
this.element.setStyle(Object.extend(this.oldStyle, {
backgroundColor: this.options.restorecolor
}));
}
});
Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
this.start(arguments[1] || {});
},
setup: function() {
Position.prepare();
var offsets = Position.cumulativeOffset(this.element);
if(this.options.offset) offsets[1] += this.options.offset;
var max = window.innerHeight ?
window.height - window.innerHeight :
document.body.scrollHeight -
(document.documentElement.clientHeight ?
document.documentElement.clientHeight : document.body.clientHeight);
this.scrollStart = Position.deltaY;
this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
},
update: function(position) {
Position.prepare();
window.scrollTo(Position.deltaX,
this.scrollStart + (position*this.delta));
}
});
/* ------------- combination effects ------------- */
Effect.Fade = function(element) {
element = $(element);
var oldOpacity = element.getInlineOpacity();
var options = Object.extend({
from: element.getOpacity() || 1.0,
to: 0.0,
afterFinishInternal: function(effect) {
if(effect.options.to!=0) return;
effect.element.hide();
effect.element.setStyle({opacity: oldOpacity});
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Appear = function(element) {
element = $(element);
var options = Object.extend({
from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
to: 1.0,
// force Safari to render floated elements properly
afterFinishInternal: function(effect) {
effect.element.forceRerendering();
},
beforeSetup: function(effect) {
effect.element.setOpacity(effect.options.from);
effect.element.show();
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Puff = function(element) {
element = $(element);
var oldStyle = {
opacity: element.getInlineOpacity(),
position: element.getStyle('position'),
top: element.style.top,
left: element.style.left,
width: element.style.width,
height: element.style.height
};
return new Effect.Parallel(
[ new Effect.Scale(element, 200,
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) {
Position.absolutize(effect.effects[0].element)
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.setStyle(oldStyle); }
}, arguments[1] || {})
);
}
Effect.BlindUp = function(element) {
element = $(element);
element.makeClipping();
return new Effect.Scale(element, 0,
Object.extend({ scaleContent: false,
scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
}
}, arguments[1] || {})
);
}
Effect.BlindDown = function(element) {
element = $(element);
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
scaleX: false,
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makeClipping();
effect.element.setStyle({height: '0px'});
effect.element.show();
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
}
}, arguments[1] || {}));
}
Effect.SwitchOff = function(element) {
element = $(element);
var oldOpacity = element.getInlineOpacity();
return new Effect.Appear(element, Object.extend({
duration: 0.4,
from: 0,
transition: Effect.Transitions.flicker,
afterFinishInternal: function(effect) {
new Effect.Scale(effect.element, 1, {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
beforeSetup: function(effect) {
effect.element.makePositioned();
effect.element.makeClipping();
},
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.undoPositioned();
effect.element.setStyle({opacity: oldOpacity});
}
})
}
}, arguments[1] || {}));
}
Effect.DropOut = function(element) {
element = $(element);
var oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left'),
opacity: element.getInlineOpacity() };
return new Effect.Parallel(
[ new Effect.Move(element, {x: 0, y: 100, sync: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
Object.extend(
{ duration: 0.5,
beforeSetup: function(effect) {
effect.effects[0].element.makePositioned();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle);
}
}, arguments[1] || {}));
}
Effect.Shake = function(element) {
element = $(element);
var oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left') };
return new Effect.Move(element,
{ x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
effect.element.undoPositioned();
effect.element.setStyle(oldStyle);
}}) }}) }}) }}) }}) }});
}
Effect.SlideDown = function(element) {
element = $(element);
element.cleanWhitespace();
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
var oldInnerBottom = $(element.firstChild).getStyle('bottom');
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
scaleX: false,
scaleFrom: window.opera ? 0 : 1,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makePositioned();
effect.element.firstChild.makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping();
effect.element.setStyle({height: '0px'});
effect.element.show(); },
afterUpdateInternal: function(effect) {
effect.element.firstChild.setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
// IE will crash if child is undoPositioned first
if(/MSIE/.test(navigator.userAgent) && !window.opera){
effect.element.undoPositioned();
effect.element.firstChild.undoPositioned();
}else{
effect.element.firstChild.undoPositioned();
effect.element.undoPositioned();
}
effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
Effect.SlideUp = function(element) {
element = $(element);
element.cleanWhitespace();
var oldInnerBottom = $(element.firstChild).getStyle('bottom');
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
restoreAfterFinish: true,
beforeStartInternal: function(effect) {
effect.element.makePositioned();
effect.element.firstChild.makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping();
effect.element.show(); },
afterUpdateInternal: function(effect) {
effect.element.firstChild.setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' }); },
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.firstChild.undoPositioned();
effect.element.undoPositioned();
effect.element.setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
return new Effect.Scale(element, window.opera ? 1 : 0,
{ restoreAfterFinish: true,
beforeSetup: function(effect) {
effect.element.makeClipping(effect.element); },
afterFinishInternal: function(effect) {
effect.element.hide(effect.element);
effect.element.undoClipping(effect.element); }
});
}
Effect.Grow = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.full
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var initialMoveX, initialMoveY;
var moveX, moveY;
switch (options.direction) {
case 'top-left':
initialMoveX = initialMoveY = moveX = moveY = 0;
break;
case 'top-right':
initialMoveX = dims.width;
initialMoveY = moveY = 0;
moveX = -dims.width;
break;
case 'bottom-left':
initialMoveX = moveX = 0;
initialMoveY = dims.height;
moveY = -dims.height;
break;
case 'bottom-right':
initialMoveX = dims.width;
initialMoveY = dims.height;
moveX = -dims.width;
moveY = -dims.height;
break;
case 'center':
initialMoveX = dims.width / 2;
initialMoveY = dims.height / 2;
moveX = -dims.width / 2;
moveY = -dims.height / 2;
break;
}
return new Effect.Move(element, {
x: initialMoveX,
y: initialMoveY,
duration: 0.01,
beforeSetup: function(effect) {
effect.element.hide();
effect.element.makeClipping();
effect.element.makePositioned();
},
afterFinishInternal: function(effect) {
new Effect.Parallel(
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
new Effect.Scale(effect.element, 100, {
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
effect.effects[0].element.setStyle({height: '0px'});
effect.effects[0].element.show();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.undoClipping();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle);
}
}, options)
)
}
});
}
Effect.Shrink = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.none
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var moveX, moveY;
switch (options.direction) {
case 'top-left':
moveX = moveY = 0;
break;
case 'top-right':
moveX = dims.width;
moveY = 0;
break;
case 'bottom-left':
moveX = 0;
moveY = dims.height;
break;
case 'bottom-right':
moveX = dims.width;
moveY = dims.height;
break;
case 'center':
moveX = dims.width / 2;
moveY = dims.height / 2;
break;
}
return new Effect.Parallel(
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
effect.effects[0].element.makePositioned();
effect.effects[0].element.makeClipping(); },
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.undoClipping();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle); }
}, options)
);
}
Effect.Pulsate = function(element) {
element = $(element);
var options = arguments[1] || {};
var oldOpacity = element.getInlineOpacity();
var transition = options.transition || Effect.Transitions.sinoidal;
var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
reverser.bind(transition);
return new Effect.Opacity(element,
Object.extend(Object.extend({ duration: 3.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
Effect.Fold = function(element) {
element = $(element);
var oldStyle = {
top: element.style.top,
left: element.style.left,
width: element.style.width,
height: element.style.height };
Element.makeClipping(element);
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
afterFinishInternal: function(effect) {
new Effect.Scale(element, 1, {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.setStyle(oldStyle);
} });
}}, arguments[1] || {}));
};
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
function(f) { Element.Methods[f] = Element[f]; }
);
Element.Methods.visualEffect = function(element, effect, options) {
s = effect.gsub(/_/, '-').camelize();
effect_class = s.charAt(0).toUpperCase() + s.substring(1);
new Effect[effect_class](element, options);
return $(element);
};
Element.addMethods();

View File

@ -0,0 +1,47 @@
function togglePasteThread(uid) {
if (Element.visible('paste_thread')) {
new Effect.Fade('paste_thread');
return;
}
new Ajax.Updater('paste_thread', '/ajax/find_paste_thread/', {
parameters: 'paste=' + uid,
method: 'get',
asynchronous: true,
onSuccess: function() {
new Effect.Appear('paste_thread');
}
});
}
function toggleLineNumbers() {
var el = document.getElementsByClassName('linenos')[0];
el.style.display = (el.style.display == '') ? 'none' : '';
}
function getPasteView() {
if (typeof _paste_view == 'undefined') {
_paste_view = document.getElementsByClassName('syntax')[0];
}
return _paste_view;
}
function toggleSyntax() {
var el = getPasteView();
el.className = (el.className == 'plain') ? 'syntax' : 'plain';
}
function selectCode() {
var el = getPasteView().getElementsByClassName('code')[0];
if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(el);
range.select();
}
else {
var range = document.createRange();
range.selectNodeContents(el);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}

2241
pastebin/media/js/prototype.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
// script.aculo.us scriptaculous.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// 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.
var Scriptaculous = {
Version: '1.6.4',
require: function(libraryName) {
// inserting via DOM fails in Safari 2.0, so brute force approach
document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
},
load: function() {
if((typeof Prototype=='undefined') ||
(typeof Element == 'undefined') ||
(typeof Element.Methods=='undefined') ||
parseFloat(Prototype.Version.split(".")[0] + "." +
Prototype.Version.split(".")[1]) < 1.5)
throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
$A(document.getElementsByTagName("script")).findAll( function(s) {
return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
}).each( function(s) {
var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
var includes = s.src.match(/\?.*load=([a-z,]*)/);
(includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
function(include) { Scriptaculous.require(path+include+'.js') });
});
}
}
Scriptaculous.load();

294
pastebin/media/js/slider.js Normal file
View File

@ -0,0 +1,294 @@
// script.aculo.us slider.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006
// Copyright (c) 2005 Marty Haught, Thomas Fuchs
//
// See http://script.aculo.us for more info
//
// 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.
if(!Control) var Control = {};
Control.Slider = Class.create();
// options:
// axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
// onChange(value)
// onSlide(value)
Control.Slider.prototype = {
initialize: function(handle, track, options) {
var slider = this;
if(handle instanceof Array) {
this.handles = handle.collect( function(e) { return $(e) });
} else {
this.handles = [$(handle)];
}
this.track = $(track);
this.options = options || {};
this.axis = this.options.axis || 'horizontal';
this.increment = this.options.increment || 1;
this.step = parseInt(this.options.step || '1');
this.range = this.options.range || $R(0,1);
this.value = 0; // assure backwards compat
this.values = this.handles.map( function() { return 0 });
this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
this.options.startSpan = $(this.options.startSpan || null);
this.options.endSpan = $(this.options.endSpan || null);
this.restricted = this.options.restricted || false;
this.maximum = this.options.maximum || this.range.end;
this.minimum = this.options.minimum || this.range.start;
// Will be used to align the handle onto the track, if necessary
this.alignX = parseInt(this.options.alignX || '0');
this.alignY = parseInt(this.options.alignY || '0');
this.trackLength = this.maximumOffset() - this.minimumOffset();
this.handleLength = this.isVertical() ?
(this.handles[0].offsetHeight != 0 ?
this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
(this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
this.handles[0].style.width.replace(/px$/,""));
this.active = false;
this.dragging = false;
this.disabled = false;
if(this.options.disabled) this.setDisabled();
// Allowed values array
this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
if(this.allowedValues) {
this.minimum = this.allowedValues.min();
this.maximum = this.allowedValues.max();
}
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.update.bindAsEventListener(this);
// Initialize handles in reverse (make sure first handle is active)
this.handles.each( function(h,i) {
i = slider.handles.length-1-i;
slider.setValue(parseFloat(
(slider.options.sliderValue instanceof Array ?
slider.options.sliderValue[i] : slider.options.sliderValue) ||
slider.range.start), i);
Element.makePositioned(h); // fix IE
Event.observe(h, "mousedown", slider.eventMouseDown);
});
Event.observe(this.track, "mousedown", this.eventMouseDown);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
this.initialized = true;
},
dispose: function() {
var slider = this;
Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
this.handles.each( function(h) {
Event.stopObserving(h, "mousedown", slider.eventMouseDown);
});
},
setDisabled: function(){
this.disabled = true;
},
setEnabled: function(){
this.disabled = false;
},
getNearestValue: function(value){
if(this.allowedValues){
if(value >= this.allowedValues.max()) return(this.allowedValues.max());
if(value <= this.allowedValues.min()) return(this.allowedValues.min());
var offset = Math.abs(this.allowedValues[0] - value);
var newValue = this.allowedValues[0];
this.allowedValues.each( function(v) {
var currentOffset = Math.abs(v - value);
if(currentOffset <= offset){
newValue = v;
offset = currentOffset;
}
});
return newValue;
}
if(value > this.range.end) return this.range.end;
if(value < this.range.start) return this.range.start;
return value;
},
setValue: function(sliderValue, handleIdx){
if(!this.active) {
this.activeHandleIdx = handleIdx || 0;
this.activeHandle = this.handles[this.activeHandleIdx];
this.updateStyles();
}
handleIdx = handleIdx || this.activeHandleIdx || 0;
if(this.initialized && this.restricted) {
if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
sliderValue = this.values[handleIdx-1];
if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
sliderValue = this.values[handleIdx+1];
}
sliderValue = this.getNearestValue(sliderValue);
this.values[handleIdx] = sliderValue;
this.value = this.values[0]; // assure backwards compat
this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
this.translateToPx(sliderValue);
this.drawSpans();
if(!this.dragging || !this.event) this.updateFinished();
},
setValueBy: function(delta, handleIdx) {
this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
handleIdx || this.activeHandleIdx || 0);
},
translateToPx: function(value) {
return Math.round(
((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
(value - this.range.start)) + "px";
},
translateToValue: function(offset) {
return ((offset/(this.trackLength-this.handleLength) *
(this.range.end-this.range.start)) + this.range.start);
},
getRange: function(range) {
var v = this.values.sortBy(Prototype.K);
range = range || 0;
return $R(v[range],v[range+1]);
},
minimumOffset: function(){
return(this.isVertical() ? this.alignY : this.alignX);
},
maximumOffset: function(){
return(this.isVertical() ?
(this.track.offsetHeight != 0 ? this.track.offsetHeight :
this.track.style.height.replace(/px$/,"")) - this.alignY :
(this.track.offsetWidth != 0 ? this.track.offsetWidth :
this.track.style.width.replace(/px$/,"")) - this.alignY);
},
isVertical: function(){
return (this.axis == 'vertical');
},
drawSpans: function() {
var slider = this;
if(this.spans)
$R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
if(this.options.startSpan)
this.setSpan(this.options.startSpan,
$R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
if(this.options.endSpan)
this.setSpan(this.options.endSpan,
$R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
},
setSpan: function(span, range) {
if(this.isVertical()) {
span.style.top = this.translateToPx(range.start);
span.style.height = this.translateToPx(range.end - range.start + this.range.start);
} else {
span.style.left = this.translateToPx(range.start);
span.style.width = this.translateToPx(range.end - range.start + this.range.start);
}
},
updateStyles: function() {
this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
Element.addClassName(this.activeHandle, 'selected');
},
startDrag: function(event) {
if(Event.isLeftClick(event)) {
if(!this.disabled){
this.active = true;
var handle = Event.element(event);
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var track = handle;
if(track==this.track) {
var offsets = Position.cumulativeOffset(this.track);
this.event = event;
this.setValue(this.translateToValue(
(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
));
var offsets = Position.cumulativeOffset(this.activeHandle);
this.offsetX = (pointer[0] - offsets[0]);
this.offsetY = (pointer[1] - offsets[1]);
} else {
// find the handle (prevents issues with Safari)
while((this.handles.indexOf(handle) == -1) && handle.parentNode)
handle = handle.parentNode;
this.activeHandle = handle;
this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
this.updateStyles();
var offsets = Position.cumulativeOffset(this.activeHandle);
this.offsetX = (pointer[0] - offsets[0]);
this.offsetY = (pointer[1] - offsets[1]);
}
}
Event.stop(event);
}
},
update: function(event) {
if(this.active) {
if(!this.dragging) this.dragging = true;
this.draw(event);
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
}
},
draw: function(event) {
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var offsets = Position.cumulativeOffset(this.track);
pointer[0] -= this.offsetX + offsets[0];
pointer[1] -= this.offsetY + offsets[1];
this.event = event;
this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
if(this.initialized && this.options.onSlide)
this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
},
endDrag: function(event) {
if(this.active && this.dragging) {
this.finishDrag(event, true);
Event.stop(event);
}
this.active = false;
this.dragging = false;
},
finishDrag: function(event, success) {
this.active = false;
this.dragging = false;
this.updateFinished();
},
updateFinished: function() {
if(this.initialized && this.options.onChange)
this.options.onChange(this.values.length>1 ? this.values : this.value, this);
this.event = null;
}
}

View File

191
pastebin/pastes/models.py Normal file
View File

@ -0,0 +1,191 @@
import sha
import math
import re
from pastebin.utils import get_external_url, get_timestamp
from django.db import models
from time import time
from random import random
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name, LEXERS
from pygments.styles import STYLE_MAP
LANGUAGES = [(x[2][0], x[1]) for x in LEXERS.itervalues()]
LANGUAGES.sort(key=lambda x: x[1].lower())
LANGUAGE_DICT = dict(LANGUAGES)
STYLES = [(x, x.title()) for x in STYLE_MAP]
STYLES.sort(key=lambda x: x[0].lower())
KNOWN_STYLES = set(STYLE_MAP)
formatter = HtmlFormatter(cssclass='syntax',
linenos=True,
linenospecial=5)
tag_strip_re = re.compile(r'[^a-zA-Z0-9][^a-zA-Z0-9_-]*')
def tagify(taglist):
"""
Ensures that only good tag names are submitted and return
an iterator of ``Tag`` objects.
"""
if isinstance(taglist, (tuple, list)):
names = taglist
else:
names = taglist.split()
found = set()
for name in names:
name = tag_strip_re.sub('', name).lower()
if name not in found:
found.add(name)
yield Tag.objects.get_or_create(name=name)[0]
def get_next_paste_uid(private):
"""
Return the next free uid for a Paste. If private is ``True``
the returned uid will be a 40bit long hash value, otherwise
a integer number as string.
"""
if private:
while True:
uid = sha.new('%s|%s' % (time(), random())).hexdigest()
try:
Paste.objects.get(uid=uid)
except Paste.DoesNotExist:
return uid
try:
last = Paste.objects.order_by('-id').filter(private=False)[0]
except IndexError:
return '1'
return str(int(last.uid) + 1)
class TagManager(models.Manager):
def get_popular(self, amount=15):
all = list(self.all())
all.sort(key=lambda x: x.count())
all.reverse()
return all[:amount]
class Tag(models.Model):
name = models.CharField(maxlength=100)
objects = TagManager()
def get_absolute_url(self):
return '/tags/%s/' % self.name
def get_size(self):
return (math.log(self.count() or 1) * 4) + 10
def count(self):
return self.paste_set.count()
def __str__(self):
return self.name
class Admin:
list_display = search_fields = ('name',)
class Meta:
ordering = ('-name',)
class Paste(models.Model):
uid = models.CharField(maxlength=40, blank=True)
private = models.BooleanField()
title = models.CharField(maxlength=200, blank=True)
author = models.CharField(maxlength=100, blank=True)
pub_date = models.DateTimeField(auto_now_add=True)
reply_to = models.ForeignKey('self', blank=True, null=True)
language = models.CharField(maxlength=50, choices=LANGUAGES,
blank=True)
code = models.TextField()
parsed_code = models.TextField(blank=True)
tags = models.ManyToManyField(Tag, blank=True)
@property
def language_name(self):
return LANGUAGE_DICT.get(self.language, 'Unknown')
@property
def code_summary(self):
try:
start = self.parsed_code.index('</pre>')
code = self.parsed_code[
self.parsed_code.index('<pre>', start) + 5:
self.parsed_code.rindex('</pre>')
].strip('\n').splitlines()
except IndexError:
code = ''.strip('\n').splitlines()
code = '\n'.join(code[:7] + ['...'])
return '<div class="syntax"><pre>%s</pre></div>' % code
def to_dict(self):
return {
'uid': self.uid,
'private': self.private,
'title': self.title,
'author': self.author,
'pub_date': get_timestamp(self.pub_date),
'reply_to': getattr(self.reply_to, 'uid', ''),
'language': self.language,
'language_name': self.language_name,
'code': self.code,
'parsed_code': self.parsed_code,
'tags': [tag.name for tag in self.tags.all()],
'url': get_external_url(self)
}
def get_absolute_url(self):
return '/show/%s/' % self.uid
def get_reply_url(self):
return '/reply/%s/' % self.uid
def get_compare_url(self):
if self.reply_to:
return '/compare/%s/' % self.uid
return ''
def save(self):
if not self.title:
self.title = 'Untitled'
if not self.author:
self.author = 'anonymous'
if not self.uid:
self.uid = get_next_paste_uid(self.private)
if not self.language:
self.language = 'text'
lexer = get_lexer_by_name(self.language)
self.code = '\n'.join(self.code.splitlines())
self.parsed_code = highlight(self.code, lexer, formatter)
super(Paste, self).save()
def __str__(self):
return 'Paste #%s by %s' % (
self.uid,
self.author
)
class Admin:
fields = (
(None, {
'fields': ('title', 'author', 'pub_date', 'language',
'code')
}),
('Extra', {
'fields': ('uid', 'private', 'reply_to')
})
)
list_display = ('uid', 'title', 'author', 'pub_date', 'language')
list_filter = ('language', 'pub_date')
search_fields = ('code', 'author', 'title')
class Meta:
ordering = ('-pub_date', 'title')

133
pastebin/pastes/views.py Normal file
View File

@ -0,0 +1,133 @@
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.forms import FormWrapper
from pastebin.utils import Pagination, templated, render_diff
from pastebin.pastes.models import Paste, Tag, tagify
@templated('pastes/new.html')
def new_paste(request, reply=None):
tags = request.POST.get('tags', '')
reply_to = None
data = {}
errors = {}
if reply:
reply_to = Paste.objects.get(uid=reply)
data['title'] = reply_to.title
if not data['title'].startswith('Re:'):
data['title'] = 'Re: ' + data['title']
data['language'] = reply_to.language
data['code'] = reply_to.code
data['private'] = reply_to.private
tags = ' '.join(tag.name for tag in reply_to.tags.all())
reply_to = reply_to.id
manipulator = Paste.AddManipulator()
if request.method == 'POST':
data = request.POST.copy()
if 'tags' in data:
del data['tags']
errors = manipulator.get_validation_errors(data)
if not errors:
request.session['author'] = data['author']
manipulator.do_html2python(data)
paste = manipulator.save(data)
for tag in tagify(tags):
paste.tags.add(tag)
return HttpResponseRedirect(paste.get_absolute_url())
if not 'author' in data:
data['author'] = request.session.get('author', '')
return {
'reply_to': reply_to,
'tags': tags,
'form': FormWrapper(manipulator, data, errors)
}
@templated('pastes/show.html')
def show_paste(request, uid):
paste = Paste.objects.get(uid=uid)
if request.GET.get('action') == 'raw':
return HttpResponse(paste.code, mimetype='text/plain')
return {
'paste': paste
}
@templated('pastes/tagcloud.html')
def tagcloud(request):
return {
'tags': Tag.objects.all()
}
@templated('pastes/taglist.html')
def taglist(request, tagname):
return {
'tag': Tag.objects.get(name=tagname)
}
@templated('pastes/all_pastes.html')
def all_pastes(request, page=1):
pagination = Pagination(Paste.objects.order_by('-pub_date')
.filter(private=False),
page, '/all/', 10)
return {
'pastes': pagination.get_objects(),
'pagination': pagination.generate()
}
def recent(request):
"""That's an old view"""
return HttpResponseRedirect('/all/')
@templated('pastes/compare.html')
def compare_paste(request, uid):
paste1 = Paste.objects.get(uid=uid)
paste2 = paste1.reply_to
if not paste2:
raise Http404()
diff = render_diff(paste2.code, paste1.code)
return {
'new': paste1,
'old': paste2,
'diff': diff
}
@templated('pastes/_autocomplete.html')
def autocomplete(request):
return {
'tags': Tag.objects.order_by('name')
.filter(name__istartswith=
request.REQUEST.get('tags', ''))
}
@templated('pastes/_find_thread.html')
def find_thread(request):
paste = node = Paste.objects.get(uid=request.REQUEST.get('paste', ''))
result = []
while node.reply_to:
result.append(node.reply_to)
node = node.reply_to
result.reverse()
backref = paste
while True:
result.append(backref)
try:
backref = Paste.objects.get(reply_to=backref)
except Paste.DoesNotExist:
break
result.reverse()
return {
'pastes': result,
'current': paste
}

80
pastebin/settings.py Normal file
View File

@ -0,0 +1,80 @@
# Django settings for pastebin project.
from os.path import join, dirname, abspath
BASE_PATH = abspath(dirname(__file__))
DEBUG = not False
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'sqlite3' # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
DATABASE_NAME = join(BASE_PATH, 'pastebin.db') # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. All choices can be found here:
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
TIME_ZONE = 'Europe/Vienna'
# Language code for this installation. All choices can be found here:
# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
# http://blogs.law.harvard.edu/tech/stories/storyReader$15
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = join(BASE_PATH, 'media')
# URL that handles the media served from MEDIA_ROOT.
# Example: "http://media.lawrence.com"
MEDIA_URL = '/media/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/admin/'
ADMIN_MEDIA_ROOT = join(dirname(__import__('django.contrib.admin',
'', '', ['']).__file__), 'media')
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'INSERT_RANDOM_KEY_HERE'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.doc.XViewMiddleware',
)
ROOT_URLCONF = 'pastebin.urls'
TEMPLATE_DIRS = (
join(BASE_PATH, 'templates'),
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'pastebin.pastes',
)

View File

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

32
pastebin/static/views.py Normal file
View File

@ -0,0 +1,32 @@
from pastebin.utils import templated
from django.http import HttpResponseRedirect
from pastebin.pastes.models import KNOWN_STYLES
@templated('static/help.html')
def help(request):
return {}
@templated('static/about.html')
def about(request):
return {}
@templated('static/error404.html')
def error404(request):
return {
'url': '/' + request.META.get('PATH_INFO', '').lstrip('/')
}
@templated('static/error500.html')
def error500(request):
return {}
def change_settings(request):
style = request.GET.get('style')
if style in KNOWN_STYLES:
request.session['style'] = style
return HttpResponseRedirect(request.META.get('HTTP_REFERER') or '/')

View File

@ -0,0 +1,64 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{% block title %}Unnamed{% endblock %} :: LodgeIt</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="{{ SETTINGS.MEDIA_URL }}css/style.css" />
<script type="text/javascript" src="{{ SETTINGS.MEDIA_URL }}js/prototype.js"></script>
<script type="text/javascript" src="{{ SETTINGS.MEDIA_URL }}js/effects.js"></script>
<script type="text/javascript" src="{{ SETTINGS.MEDIA_URL }}js/controls.js"></script>
<script type="text/javascript" src="{{ SETTINGS.MEDIA_URL }}js/pastebin.js"></script>
{% block head %}{% endblock %}
<style type="text/css">
{{ STYLE|escape }}
</style>
</head>
<body>
<div class="header">
<h1><a href="/">LodgeIt</a></h1>
<ul>
<li><a href="/">New</a></li>
<li><a href="/tags/">Tags</a></li>
<li><a href="/all/">All</a></li>
<li><a href="/help/">Help</a></li>
<li><a href="/about/">About</a></li>
</ul>
</div>
<div class="content">
<div class="contentwrapper">
{% block content %}{% endblock %}
</div>
</div>
<div class="sidebar">
<div class="contentwrapper">
{% block sidebar %}{% endblock %}
<h3>Style</h3>
<form action="/change_settings/" method="get">
<select name="style" onchange="this.parentNode.submit()">
{% for style in STYLES %}
<option value="{{ style.value|escape }}"{% if style.selected %} selected="selected"{% endif %}>{{ style.caption|escape }}</option>
{% endfor %}
</select>
</form>
<h3>Popular Tags</h3>
<div class="tagcloud">
{% for tag in TAGS %}
<a href="{{ tag.get_absolute_url|escape }}" style="font-size: {{ tag.get_size }}px" title="{{ tag.name|escape }}: {{ tag.count }}">{{ tag.name|escape }}</a>
{% endfor %}
</div>
<h3>Recent Pastes</h3>
<ul>
{% for paste in RECENT %}
<li><a href="{{ paste.get_absolute_url }}">{{ paste.title|escape }}</a> by {{ paste.author|escape }}</li>
{% endfor %}
</ul>
</div>
</div>
</html>

View File

@ -0,0 +1,5 @@
<ul>
{% for tag in tags %}
<li>{{ tag.name|escape }}</li>
{% endfor %}
</ul>

View File

@ -0,0 +1,10 @@
<ul>
{% for paste in pastes %}
<li>{% ifequal paste current %}
<strong>{{ paste.title|escape }}</a></strong>
{% else %}
<a href="{{ paste.get_absolute_url }}">{{ paste.title|escape }}</a>
{% endifequal %}
by {{ paste.author|escape }}</li>
{% endfor %}
</ul>

View File

@ -0,0 +1,27 @@
{% extends "layout.html" %}
{% block title %}All Pastes{% endblock %}
{% block content %}
{% if pastes %}
<h2>All Pastes</h2>
<div class="pagination">
<strong>Go To:</strong> {{ pagination }}
</div>
<ul>
{% for paste in pastes %}
<li class="paste_summary">
<p><a href="{{ paste.get_absolute_url }}">{{ paste.title|escape }}</a> by {{ paste.author|escape }} on {{ paste.pub_date|date:"j. N Y H:i" }}</p>
<a href="{{ paste.get_absolute_url }}" class="detail">{{ paste.code_summary }}</a>
</li>
{% endfor %}
</ul>
<div class="pagination">
<strong>Go To:</strong> {{ pagination }}
</div>
{% else %}
<ul><li><em>no pastes so far</em></li></ul>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "layout.html" %}
{% block title %}Compare Paste{% endblock %}
{% block content %}
<h2>Compare Paste</h2>
<table class="diff">
<tr class="table_header">
<th colspan="3">Old - {{ old.title|escape }} by {{ old.author|escape }}</th>
<th colspan="3">New - {{ new.title|escape }} by {{ new.author|escape }}</th>
</tr>
{{ diff }}
</table>
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends "layout.html" %}
{% block title %}New Paste{% endblock %}
{% block content %}
<h2>New Paste</h2>
<form action="." method="post">
{% if reply_to %}
<input type="hidden" name="reply_to" value="{{ reply_to }}" />
{% endif %}
<dl>
<dt>Title</dt>
<dd>{{ form.title }}{% if form.title.errors %}
<p class="error"><strong>Error:</strong>
{{ form.title.errors|join:", " }}
</p>
{% endif %}</dd>
<dt>Author</dt>
<dd>{{ form.author }}{% if form.author.errors %}
<p class="error"><strong>Error:</strong>
{{ form.author.errors|join:", " }}
</p>
{% endif %}</dd>
<dt>Language</dt>
<dd>{{ form.language }}{% if form.language.errors %}
<p class="error"><strong>Error:</strong>
{{ form.language.errors|join:", " }}
</p>
{% endif %}</dd>
<dt>Private</dt>
<dd>{{ form.private }}{% if form.private.errors %}
<p class="error"><strong>Error:</strong>
{{ form.private.errors|join:", " }}
</p>
{% endif %}</dd>
<dt>Tags</dt>
<dd><input type="text" name="tags" id="tags" value="{{ tags|escape }}" />
<div class="autocomplete" id="tags_auto_complete"></div>
<script type="text/javascript">
new Ajax.Autocompleter(
'tags',
'tags_auto_complete',
'/ajax/tags_auto_complete/', {
tokens: ' '
}
);
</script></dd>
</dl>
{{ form.code }}
{% if form.code.errors %}
<p class="error"><strong>Error:</strong>
{{ form.code.errors|join:", " }}
</p>
{% endif %}
<div class="action">
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "layout.html" %}
{% block title %}Recent Pastes{% endblock %}
{% block content %}
<h2>Recent Pastes</h2>
<ul>
{% for paste in pastes %}
<li class="paste_summary">
<p><a href="{{ paste.get_absolute_url }}">{{ paste.title|escape }}</a> by {{ paste.author|escape }} on {{ paste.pub_date|date:"j. N Y H:i" }}</p>
<a href="{{ paste.get_absolute_url }}" class="detail">{{ paste.code_summary }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "layout.html" %}
{% block title %}{{ paste.title|escape }} by {{ paste.author|escape }}{% endblock %}
{% block sidebar %}
<h3>This Paste</h3>
<dl>
<dt>Author</dt>
<dd>{{ paste.author|escape }}</dd>
<dt>Language</dt>
<dd>{{ paste.language_name|escape }}</dd>
<dt>Date</dt>
<dd>{{ paste.pub_date|date:"j. N Y H:i" }}</dd>
<dt>Tags</dt>
<dd>{% if not paste.tags.all %}<em>not tagged</em>{% endif %}
{% for tag in paste.tags.all %}
<a href="{{ tag.get_absolute_url }}" title="{{ tag.name|escape }}: {{ tag.count }}">{{ tag.name|escape }}</a>{% if not forloop.last %},{% endif %}
{% endfor %}</dd>
{% if paste.reply_to %}
<dt>Reply To</dt>
<dd><a href="{{ paste.reply_to.get_absolute_url }}">{{ paste.reply_to.title|escape }}</a> by {{ paste.reply_to.author|escape }}
{% endif %}
</dl>
<h3>Actions</h3>
<ul>
<li><a href="?action=raw">download paste</a></li>
<li><a href="{{ paste.get_reply_url }}">reply to this paste</a></li>
{% if paste.reply_to %}
<li><a href="{{ paste.get_compare_url }}">compare with original</a>
{% endif %}
<li><a href="javascript:toggleLineNumbers()">toggle line numbers</a></li>
<li><a href="javascript:toggleSyntax()">toggle syntax highlighting</a></li>
<li><a href="javascript:selectCode()">select code</a></li>
<li><a href="javascript:togglePasteThread('{{ paste.uid }}')">toggle paste thread</a></li>
</ul>
<div id="paste_thread" class="pastethread"></div>
<script type="text/javascript">
Element.hide('paste_thread');
</script>
{% endblock %}
{% block content %}
<h2>{{ paste.title|escape }}</h2>
{{ paste.parsed_code }}
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "layout.html" %}
{% block title %}Tags{% endblock %}
{% block content %}
<h2>Tags</h2>
<div class="tagcloud">
{% for tag in tags %}
<a href="{{ tag.get_absolute_url|escape }}" style="font-size: {{ tag.get_size }}px">{{ tag.name|escape }}</a>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "layout.html" %}
{% block title %}Pastes tagged {{ tag.name|escape }}{% endblock %}
{% block sidebar %}
<h3>Tags</h3>
<ul>
<li><a href="/tags/">go to tagcloud</a></li>
</ul>
{% endblock %}
{% block content %}
<h2>Pastes tagged {{ tag.name|escape }}</h2>
<ul>
{% for paste in tag.paste_set.all %}
<li><a href="{{ paste.get_absolute_url }}">{{ paste.title|escape }}</a> by {{ paste.author|escape }} on {{ paste.pub_date|date:"j. N Y H:i" }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,50 @@
{% extends "layout.html" %}
{% block title %}About{% endblock %}
{% block content %}
<h2>About</h2>
<h3>Why the hell another pastebin?</h3>
<p>
Good question. Basically the world doesn't need another pastebin.
There is <a href="http://pastie.caboo.se/">pastie</a> and
<a href="http://paste.e-scribe.com/">paste.e-scribe.com</a> which
both use kick-ass highlighting libraries for highlighting the
code and both have an initiutive user interface. Nevertheless I
had too much free time and created this pastebin.
</p>
<p>
Feel free to not use it ;-)
</p>
<h3>Features</h3>
<ul>
<li><a href="/tags/">Tags</a></li>
<li>private pastes</li>
<li>web2.0 design ^^</li>
<li>different color schemes for the sourcecode</li>
<li>toggleable line numbers/syntax highlighting</li>
<li>reply to pastes with different highlighting</li>
<li>support for many python template languages</li>
<li>support for many scripting languages like python and ruby</li>
<li><a href="/xmlrpc/">XMLRPC support</a></li>
</ul>
<h3>Software Used</h3>
<ul>
<li><a href="http://www.sqlite.org/">sqlite3</a> as database</li>
<li><a href="http://www.djangoproject.com/">django</a> as framework</li>
<li><a href="http://pygments.pocoo.org/">pygments</a> for syntax highlighting</li>
<li><a href="http://www.python.org/">python</a> as scripting language</a>
</ul>
<h3>Who?</h3>
<p>
<a href="http://lucumr.pocoo.org/">mitsuhiko</a> from the pocoo
team is responsible for the pastebin. Pygments is a pocoo project
led by Georg Brandl.
</p>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "layout.html" %}
{% block title %}Page Not Found{% endblock %}
{% block content %}
<h2>Page Not Found</h2>
<p>
Sorry. The page '{{ url|escape }}' doesn't exist on this server.
</p>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "layout.html" %}
{% block title %}Internal Server Error{% endblock %}
{% block content %}
<h2>Internal Server Error</h2>
<p>
The server encountered an internal error or misconfiguration
and was unable to complete your request.
</p>
{% endblock %}

View File

@ -0,0 +1,67 @@
{% extends "layout.html" %}
{% block title %}Help{% endblock %}
{% block sidebar %}
<h3>Scripts</h3>
<ul>
<li><a href="/show/lodgeit.py">lodgeit.py</a> - python script
to paste from the command line
(<a href="/show/lodgeit.py?action=raw">download</a>)</li>
</ul>
<h3>Libraries</h3>
<ul>
<li><a href="/show/lodgeit.rb">lodgeit.rb</a> - ruby library
for lodgeit
(<a href="/show/lodgeit.rb?action=raw">download</a>)</li>
</ul>
{% endblock %}
{% block content %}
<h2>Help</h2>
<h3>Pasting Sourcecode</h3>
<p>
You can simply paste sourcecode without logging in or something
else. Just go to the <a href="/">new paste</a> page and enter
your sourcecode and the language it's written in. If you mark
your paste as private it gets an 40 bit long unique hash which is
hard to guess and the paste won't appear in the recent changes
list. But if you give your paste a tag it will appear in the
tag cloud/list. Nevertheless you shouldn't paste private information
in this pastebin because someone might get the link to a private
paste by sniffing your internet connection etc.
</p>
<p>
LodgeIt automatically saves your name in a browser session together
with your syntax highlighting scheme. That data is deleted as soon
as you close your browser or the session expires. Currently there
is no way to save your user information for longer time periods
or to register a nickname just for you.
</p>
<h3>Using Tags</h3>
<p>
You can give your paste a whitspace delimited list of tags. An
automatic completion helps you finding tags that are already in
use. Those tags can help you to find your snippet again if you
think it's useful. Please don't tag your sourcecode with tags
that have nothing to do with your sourcecode.
</p>
<h3>Writing Replies</h3>
<p>
If you click on the "reply to this paste" link on a detail page you
can change the paste and save a new version. LodgeIt saveas a version
information then so that you can compare the paste with the previous
version.
</p>
<h3>XML RPC Interface</h3>
<p>
This pastebin supports xmlrpc. Head over to the
<a href="/xmlrpc/">API</a> documentation.
</p>
{% endblock %}

View File

@ -0,0 +1,59 @@
{% extends "layout.html" %}
{% block title %}XMLRPC{% endblock %}
{% block content %}
<h2>XMLRPC</h2>
<h3>Connecting To The XMLRPC Interface</h3>
<p>
The XMLRPC Interface is available at
<tt>{{ interface_url|escape }}</tt>
</p>
<p>
From python you can connect to it using the following
sourcecode:
</p>
<pre>from xmlrpclib import ServerProxy
s = ServerProxy('{{ interface_url|escape }}')
s.pastes.method('parameter')</pre>
<h3>Public Methods</h3>
<ul>
{% for method in methods %}
<li><strong>{{ method.name|escape }}</strong>
<em>{{ method.signature|escape }}</em>
{{ method.doc|linebreaks }}</li>
{% endfor %}
</ul>
<h3>Example Usage</h3>
<p>
Here an example python session:
</p>
<pre>{% filter escape %}>>> import xmlrpclib
>>> s = xmlrpclib.ServerProxy("{{ interface_url }}")
>>> s.pastes.countPublic()
8
>>> s.pastes.getAliasForMimetype("text/x-javascript")
'js'
>>> s.pastes.getNameByAlias("js")
'JavaScript'
>>> s.pastes.newPaste("python", "print 'Hello World'")
{'code': "print 'Hello World'",
'uid': '1',
'language': 'python',
'title': 'Untitled',
'url': 'http://example.com/show/1/',
'author': 'anonymous',
'private': False,
'parsed_code': '...<span class="s">&#39;Hello World&#39;</span>...',
'reply_to': '',
'language_name':
'Unknown',
'pub_date': 1163534604,
'tags': []}
{% endfilter %}</pre>
{% endblock %}

43
pastebin/urls.py Normal file
View File

@ -0,0 +1,43 @@
from re import escape
from django.conf import settings
from django.conf.urls.defaults import *
urlpatterns = patterns('',
# Index
(r'^$', 'pastebin.pastes.views.new_paste'),
(r'^reply/([^/]+)/$', 'pastebin.pastes.views.new_paste'),
(r'^show/([^/]+)/$', 'pastebin.pastes.views.show_paste'),
(r'^compare/([^/]+)/$', 'pastebin.pastes.views.compare_paste'),
(r'^tags/$', 'pastebin.pastes.views.tagcloud'),
(r'^tags/([^/]+)/$', 'pastebin.pastes.views.taglist'),
(r'recent/$', 'pastebin.pastes.views.recent'),
(r'^all/$', 'pastebin.pastes.views.all_pastes'),
(r'^all/(\d+)/$', 'pastebin.pastes.views.all_pastes'),
# Static Pages
(r'^change_settings/$', 'pastebin.static.views.change_settings'),
(r'^about/$', 'pastebin.static.views.about'),
(r'^help/$', 'pastebin.static.views.help'),
# Ajax Functions
(r'^ajax/tags_auto_complete/$', 'pastebin.pastes.views.autocomplete'),
(r'^ajax/find_paste_thread/$', 'pastebin.pastes.views.find_thread'),
# XMLRPC
(r'^xmlrpc/$', 'pastebin.xmlrpc.views.handle_request'),
# Admin and Media
(r'^%s/(.*)$' % escape(settings.MEDIA_URL.strip('/')),
'django.views.static.serve', {
'document_root': settings.MEDIA_ROOT
}),
(r'^%s/(.*)$' % escape(settings.ADMIN_MEDIA_PREFIX.strip('/')),
'django.views.static.serve', {
'document_root': settings.ADMIN_MEDIA_ROOT
}),
(r'^admin/', include('django.contrib.admin.urls')),
)
handler404 = 'pastebin.static.views.error404'
handler500 = 'pastebin.static.views.error500'

178
pastebin/utils.py Normal file
View File

@ -0,0 +1,178 @@
import math
import inspect
import time
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
from difflib import HtmlDiff
from cgi import escape
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse, Http404
from django.template import Context, loader
from django.conf import settings
from django.contrib.sites.models import Site
from pygments.formatters import HtmlFormatter
class TemplateResponse(HttpResponse):
def get_globals(self, request):
from pastebin.pastes.models import STYLES, Paste, Tag
styles = []
current_style = request.session.get('style') or 'pastie'
style = None
for key, value in STYLES:
if style is None and key == current_style:
style = HtmlFormatter(style=key)
styles.append({
'value': key,
'caption': value,
'selected': key == current_style
})
return {
'SETTINGS': settings,
'STYLES': styles,
'STYLE': style.get_style_defs('.syntax'),
'RECENT': Paste.objects.order_by('-pub_date') \
.filter(private=False)[:5],
'TAGS': Tag.objects.get_popular()
}
def __init__(self, request, template_name, context={}, mimetype=None):
t = loader.get_template(template_name)
context.update(self.get_globals(request))
response = t.render(Context(context))
HttpResponse.__init__(self, response, mimetype)
def templated(template_name):
def decorator(f):
def proxy(*args, **kwargs):
request = args[0]
try:
rv = f(*args, **kwargs)
except ObjectDoesNotExist:
raise Http404()
if isinstance(rv, HttpResponse):
return rv
return TemplateResponse(request, template_name, rv)
return proxy
return decorator
class SaneHtmlDiff(HtmlDiff):
_table_template = '''%(data_rows)s'''
def _format_line(self, side, flag, linenum, text):
try:
linenum = '%d' % linenum
except:
pass
return ('<td class="diff_header">%s</td>'
'<td class="diff_data">%s</td>') % (
linenum, escape(text)
)
def render_diff(source1, source2):
lines1 = source1.splitlines()
lines2 = source2.splitlines()
d = SaneHtmlDiff()
return d.make_table(lines1, lines2)
class Pagination(object):
def __init__(self, query, page, link, per_page=10):
self.query = query
self.page = int(page)
self.link = link
self.per_page = per_page
def get_objects(self):
idx = (self.page - 1) * self.per_page
result = self.query[idx:idx + self.per_page]
if self.page != 1 and not result:
raise Http404()
return result
def generate(self, normal='<a href="%(href)s">%(page)d</a>',
active='<strong>%(page)d</strong>',
commata=',\n',
ellipsis=' ...\n',
threshold=3):
was_ellipsis = False
result = []
total = self.query.count()
pages = int(math.ceil(total / float(self.per_page)))
for num in xrange(1, pages + 1):
if num <= threshold or num > pages - threshold or \
abs(self.page - num) < math.ceil(threshold / 2.0):
if result and result[-1] != ellipsis:
result.append(commata)
was_space = False
if num == 0:
link = self.link
else:
link = '%s%d/' % (self.link, num)
if num == self.page:
template = active
else:
template = normal
result.append(template % {
'href': link,
'page': num
})
elif not was_ellipsis:
was_ellipsis = True
result.append(ellipsis)
return ''.join(result)
class XMLRPCRequestHandler(SimpleXMLRPCDispatcher):
def handle_request(self, request):
response = self._marshaled_dispatch(request.raw_post_data)
return HttpResponse(response, mimetype='text/xml')
def get_public_methods(self):
if not hasattr(self, '_public_methods'):
result = []
for name, f in self.funcs.iteritems():
if name.startswith('system.'):
continue
args, varargs, varkw, defaults = inspect.getargspec(f)
result.append({
'name': name,
'doc': inspect.getdoc(f) or '',
'signature': inspect.formatargspec(
args, varargs, varkw, defaults,
formatvalue=lambda o: '=' + repr(o)
)
})
result.sort(key=lambda x: x['name'].lower())
self._public_methods = result
return self._public_methods
xmlrpc = XMLRPCRequestHandler()
xmlrpc.register_introspection_functions()
def external(name):
"""Make a function external available via xmlrpc."""
def proxy(f):
xmlrpc.register_function(f, name)
return f
return proxy
def get_timestamp(d):
"""Return the UNIX timestamp of a datetime object"""
return int(time.mktime(d.timetuple()) + d.microsecond / 1e6)
def get_external_url(obj):
"""Return the external url to an object."""
return 'http://%s%s' % (
Site.objects.get_current().domain,
obj.get_absolute_url()
)

View File

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

207
pastebin/xmlrpc/views.py Normal file
View File

@ -0,0 +1,207 @@
# -*- coding: utf-8 -*-
from pygments.lexers import get_lexer_by_name, \
get_lexer_for_mimetype, \
get_lexer_for_filename
from pygments.formatters import HtmlFormatter
from pastebin.utils import templated, xmlrpc, external, get_timestamp, \
get_external_url
from pastebin.pastes.models import Paste, Tag, LANGUAGES, LANGUAGE_DICT, \
KNOWN_STYLES, tagify
@external('pastes.getLanguages')
def pastes_get_supported_languages():
"""Return an array of supported languages.
The returned array is an array of (key, value)
arrays, where key is one of the internal language
names and value the human readable name.
"""
return LANGUAGES
@external('pastes.getNameByAlias')
def pastes_get_name_by_alias(alias):
"""Return the human readable name of an language by
alias or return an empty string in case of failure."""
try:
lexer = get_lexer_by_name(alias)
except ValueError:
return ''
return lexer.name
@external('pastes.getAliasForMimetype')
def pastes_get_alias_for_mimetype(mimetype):
"""Return the alias for an lexer for a mimetype or
an empty string if not found."""
try:
lexer = get_lexer_for_mimetype(mimetype)
except ValueError:
return ''
return lexer.aliases[0]
@external('pastes.getAliasForFilename')
def pastes_get_alias_for_filename(filename):
"""Return the alias for a filename. If not found
the return value will be an empty string."""
try:
lexer = get_lexer_for_filename(filename)
except ValueError:
return ''
return lexer.aliases[0]
@external('pastes.pasteExists')
def pastes_paste_exists(uid):
"""Check if a paste exists."""
try:
Paste.objects.get(uid=uid)
except Paste.DoesNotExist:
return False
return True
@external('pastes.getPaste')
def pastes_get_paste(uid):
"""Return the details of a paste for a given
uid as an hash. If the paste is not found the
return value is boolean false.
Note that the uid is in fact a string!"""
try:
if not isinstance(uid, basestring):
uid = None
paste = Paste.objects.get(uid=uid)
except Paste.DoesNotExist:
return False
return paste.to_dict()
@external('pastes.getURL')
def pastes_get_url(uid):
"""Return the url to the paste or an empty
string if the paste does not exists."""
try:
return get_external_url(Paste.objects.get(uid=uid))
except Paste.DoesNotExists:
return ''
@external('pastes.getRecent')
def pastes_get_recent(n=1):
"""Return a list of the recent 'n' pastes.
The maximal allowed number is 50. Note that private
pastes won't be part of that list."""
n = min(50, n)
return [paste.to_dict() for paste in
Paste.objects.filter(private=False)[:n]]
@external('pastes.getPastesForTag')
def get_pastes_for_tag(tagname):
"""Return all pastes for a tag."""
try:
tag = Tag.objects.get(name=tagname)
except Tag.DoesNotExist:
return []
return [p.to_dict() for p in tag.paste_set.all()]
@external('pastes.newPaste')
def pastes_new_paste(language, code, private=False, title='Untitled',
author='anonymous', tags=[]):
"""Create a new paste. language must be a known alias,
code the piece of code you want to upload, private a
boolean indicating if you want a private upload, title
and author are strings for the details and tags a list
of tag names for this paste.
Return 0 if something goes wrong, else an dict with the
uid and url."""
try:
if not code.strip():
raise ValueError()
paste = Paste(
language=language,
code=code,
private=private,
title=title,
author=author,
)
paste.save()
except:
return 0
for tag in tagify(tags):
paste.tags.add(tag)
return {
'uid': paste.uid,
'url': get_external_url(paste)
}
@external('pastes.countPastes')
def pastes_count_pastes():
"""Return the total amount of pastes."""
return Paste.objects.count()
@external('pastes.countPrivate')
def pastes_count_private():
"""Return the total amount of private pastes."""
return Paste.objects.filter(private=True).count()
@external('pastes.countPublic')
def pastes_count_public():
"""Return the total amount of public pastes."""
return Paste.objects.filter(private=False).count()
@external('styles.getStyle')
def styles_get_style(name, prefix=''):
"""Return a string containing CSS rules for the CSS classes
used by the formatter.
The argument prefix can be used to
specify additional CSS selectors that are prepended to the
classes.
"""
try:
formatter = HtmlFormatter(style=name)
except ValueError:
return ''
return formatter.get_style_defs(prefix)
@external('styles.getStyleList')
def styles_get_style_list():
"""Return a list of known styles."""
return sorted(KNOWN_STYLES)
@external('styles.styleExists')
def styles_style_exists(name):
"""Check if a style exists."""
return name in KNOWN_STYLES
@external('tags.getTagCloud')
def tags_get_tag_cloud():
"""Return a tag cloud (unsorted)"""
return [{
'name': tag.name,
'size': tag.get_size(),
'count': tag.count()
} for tag in Tag.objects.all()]
@templated('xmlrpc/index.html')
def handle_request(request):
if request.method == 'POST':
return xmlrpc.handle_request(request)
return {
'methods': xmlrpc.get_public_methods(),
'interface_url': 'http://%s/xmlrpc/' % request.META['SERVER_NAME']
}