Compare commits
No commits in common. "flask-convert" and "master" have entirely different histories.
flask-conv
...
master
13
.bzrignore
@ -1,8 +1,9 @@
|
|||||||
|
data
|
||||||
|
.eric4project
|
||||||
|
ScribeEngine.e4p
|
||||||
|
scribeengine.sqlite
|
||||||
|
posts.sql
|
||||||
|
*.egg-info
|
||||||
|
ScrivbeEngine.e4p
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
*.pyc
|
|
||||||
__pycache__
|
|
||||||
*.egg-info
|
|
||||||
config.ini
|
|
||||||
scribeengine.sqlite
|
|
||||||
.env
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_space = true
|
|
||||||
max_line_length = 120
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[*.py]
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.html,*.css,*.js,*.ts]
|
|
||||||
indent_size = 2
|
|
8
.gitignore
vendored
@ -1,8 +0,0 @@
|
|||||||
*.egg-info
|
|
||||||
__pycache__
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
*.sqlite
|
|
||||||
venv
|
|
||||||
config.ini
|
|
3
MANIFEST.in
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
include scribeengine/config/deployment.ini_tmpl
|
||||||
|
recursive-include scribeengine/public *
|
||||||
|
recursive-include scribeengine/templates *
|
20
README.rst
@ -1,20 +0,0 @@
|
|||||||
ScribeEngine
|
|
||||||
============
|
|
||||||
|
|
||||||
ScribeEngine is an open source blog engine written in Python and Flask.
|
|
||||||
|
|
||||||
Installation and Setup
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Install ScribeEngine using ``pip``::
|
|
||||||
|
|
||||||
pip install ScribeEngine
|
|
||||||
|
|
||||||
Run the setup wizard to create a config file::
|
|
||||||
|
|
||||||
$ python -m scribeengine.config
|
|
||||||
|
|
||||||
Run a local server::
|
|
||||||
|
|
||||||
$ python -m scribeengine
|
|
||||||
|
|
19
README.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
This file is for you to describe the ScribeEngine application. Typically
|
||||||
|
you would include information such as the information below:
|
||||||
|
|
||||||
|
Installation and Setup
|
||||||
|
======================
|
||||||
|
|
||||||
|
Install ``ScribeEngine`` using easy_install::
|
||||||
|
|
||||||
|
easy_install ScribeEngine
|
||||||
|
|
||||||
|
Make a config file as follows::
|
||||||
|
|
||||||
|
paster make-config ScribeEngine config.ini
|
||||||
|
|
||||||
|
Tweak the config file as appropriate and then setup the application::
|
||||||
|
|
||||||
|
paster setup-app config.ini
|
||||||
|
|
||||||
|
Then you are ready to go.
|
@ -1,30 +0,0 @@
|
|||||||
[flask]
|
|
||||||
secret_key = changeme
|
|
||||||
|
|
||||||
[sqlalchemy]
|
|
||||||
database_uri = sqlite:///scribeengine.sqlite
|
|
||||||
track_modifications = false
|
|
||||||
|
|
||||||
[mail]
|
|
||||||
username = email@example.com
|
|
||||||
password = password
|
|
||||||
default_sender = sender <noreply@example.com>
|
|
||||||
server = smtp.gmail.com
|
|
||||||
port = 465
|
|
||||||
use_ssl = true
|
|
||||||
use_tls = false
|
|
||||||
|
|
||||||
[theme]
|
|
||||||
paths =
|
|
||||||
default =
|
|
||||||
|
|
||||||
[user]
|
|
||||||
app_name = ScribeEngine
|
|
||||||
enable_email = true
|
|
||||||
enable_username = false
|
|
||||||
email_sender_email = noreply@example.com
|
|
||||||
|
|
||||||
[site]
|
|
||||||
name = Example Blog
|
|
||||||
slogan = An example of a blog
|
|
||||||
about = This is an example of a blog using the ScribeEngine blogging engine
|
|
108
development.ini
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#
|
||||||
|
# ScribeEngine - Pylons development environment configuration
|
||||||
|
#
|
||||||
|
# The %(here)s variable will be replaced with the parent directory of this file
|
||||||
|
#
|
||||||
|
[DEFAULT]
|
||||||
|
debug = true
|
||||||
|
# Uncomment and replace with the address which should receive any error reports
|
||||||
|
#email_to = you@yourdomain.com
|
||||||
|
smtp_server = localhost
|
||||||
|
error_email_from = paste@localhost
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:Paste#http
|
||||||
|
host = 0.0.0.0
|
||||||
|
port = 5000
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = egg:ScribeEngine
|
||||||
|
full_stack = true
|
||||||
|
static_files = true
|
||||||
|
|
||||||
|
cache_dir = %(here)s/data
|
||||||
|
beaker.session.key = scribeengine
|
||||||
|
beaker.session.secret = somesecret
|
||||||
|
beaker.session.timeout = 1209600
|
||||||
|
|
||||||
|
# If you'd like to fine-tune the individual locations of the cache data dirs
|
||||||
|
# for the Cache data, or the Session saves, un-comment the desired settings
|
||||||
|
# here:
|
||||||
|
#beaker.cache.data_dir = %(here)s/data/cache
|
||||||
|
#beaker.session.data_dir = %(here)s/data/sessions
|
||||||
|
|
||||||
|
# SQLAlchemy database URL
|
||||||
|
sqlalchemy.url = sqlite:///%(here)s/scribeengine.sqlite
|
||||||
|
|
||||||
|
# Media upload directory
|
||||||
|
paths.media = %(here)s/scribeengine/public/files
|
||||||
|
# Themes directory
|
||||||
|
paths.themes = %(here)s/themes
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
security.salt = secretsalt
|
||||||
|
|
||||||
|
# Mail server settings
|
||||||
|
mail.on = false
|
||||||
|
mail.manager = immediate
|
||||||
|
mail.transport = smtp
|
||||||
|
mail.smtp.server = mail.mydomain.com
|
||||||
|
mail.smtp.username = mymailusername
|
||||||
|
mail.smtp.password = mymailpassword
|
||||||
|
|
||||||
|
# Server-related settings
|
||||||
|
server.timezone = 'Africa/Johannesburg'
|
||||||
|
|
||||||
|
# Setup options
|
||||||
|
setup.create_tables = true
|
||||||
|
setup.create_user = false
|
||||||
|
setup.blog_details = false
|
||||||
|
|
||||||
|
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
|
||||||
|
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
|
||||||
|
# execute malicious code after an exception is raised.
|
||||||
|
#set debug = false
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root, routes, scribeengine, sqlalchemy
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = INFO
|
||||||
|
handlers = console
|
||||||
|
|
||||||
|
[logger_routes]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = routes.middleware
|
||||||
|
# "level = DEBUG" logs the route matched and routing variables.
|
||||||
|
|
||||||
|
[logger_scribeengine]
|
||||||
|
level = DEBUG
|
||||||
|
handlers =
|
||||||
|
qualname = scribeengine
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
# "level = INFO" logs SQL queries.
|
||||||
|
# "level = DEBUG" logs SQL queries and results.
|
||||||
|
# "level = WARN" logs neither. (Recommended for production systems.)
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
19
docs/index.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
scribeengine
|
||||||
|
++++++++++++
|
||||||
|
|
||||||
|
This is the main index page of your documentation. It should be written in
|
||||||
|
`reStructuredText format <http://docutils.sourceforge.net/rst.html>`_.
|
||||||
|
|
||||||
|
You can generate your documentation in HTML format by running this command::
|
||||||
|
|
||||||
|
setup.py pudge
|
||||||
|
|
||||||
|
For this to work you will need to download and install `buildutils`_,
|
||||||
|
`pudge`_, and `pygments`_. The ``pudge`` command is disabled by
|
||||||
|
default; to ativate it in your project, run::
|
||||||
|
|
||||||
|
setup.py addcommand -p buildutils.pudge_command
|
||||||
|
|
||||||
|
.. _buildutils: http://pypi.python.org/pypi/buildutils
|
||||||
|
.. _pudge: http://pudge.lesscode.org/
|
||||||
|
.. _pygments: http://pygments.org/
|
276
ez_setup.py
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
#!python
|
||||||
|
"""Bootstrap setuptools installation
|
||||||
|
|
||||||
|
If you want to use setuptools in your package's setup.py, just include this
|
||||||
|
file in the same directory with it, and add this to the top of your setup.py::
|
||||||
|
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
If you want to require a specific version of setuptools, set a download
|
||||||
|
mirror, or use an alternate download directory, you can do so by supplying
|
||||||
|
the appropriate options to ``use_setuptools()``.
|
||||||
|
|
||||||
|
This file can also be run as a script to install or upgrade setuptools.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
DEFAULT_VERSION = "0.6c9"
|
||||||
|
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
|
||||||
|
|
||||||
|
md5_data = {
|
||||||
|
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
|
||||||
|
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
|
||||||
|
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
|
||||||
|
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
|
||||||
|
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
|
||||||
|
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
|
||||||
|
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
|
||||||
|
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
|
||||||
|
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
|
||||||
|
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
|
||||||
|
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
|
||||||
|
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
|
||||||
|
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
|
||||||
|
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
|
||||||
|
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
|
||||||
|
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
|
||||||
|
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
|
||||||
|
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
|
||||||
|
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
|
||||||
|
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
|
||||||
|
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
|
||||||
|
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
|
||||||
|
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
|
||||||
|
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
|
||||||
|
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
|
||||||
|
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
|
||||||
|
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
|
||||||
|
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
|
||||||
|
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
|
||||||
|
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
|
||||||
|
'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
|
||||||
|
'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
|
||||||
|
'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
|
||||||
|
'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
|
||||||
|
}
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
try: from hashlib import md5
|
||||||
|
except ImportError: from md5 import md5
|
||||||
|
|
||||||
|
def _validate_md5(egg_name, data):
|
||||||
|
if egg_name in md5_data:
|
||||||
|
digest = md5(data).hexdigest()
|
||||||
|
if digest != md5_data[egg_name]:
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"md5 validation of %s failed! (Possible download problem?)"
|
||||||
|
% egg_name
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def use_setuptools(
|
||||||
|
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
|
||||||
|
download_delay=15
|
||||||
|
):
|
||||||
|
"""Automatically find/download setuptools and make it available on sys.path
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end with
|
||||||
|
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
|
||||||
|
it is not already available. If `download_delay` is specified, it should
|
||||||
|
be the number of seconds that will be paused before initiating a download,
|
||||||
|
should one be required. If an older version of setuptools is installed,
|
||||||
|
this routine will print a message to ``sys.stderr`` and raise SystemExit in
|
||||||
|
an attempt to abort the calling script.
|
||||||
|
"""
|
||||||
|
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
|
||||||
|
def do_download():
|
||||||
|
egg = download_setuptools(version, download_base, to_dir, download_delay)
|
||||||
|
sys.path.insert(0, egg)
|
||||||
|
import setuptools; setuptools.bootstrap_install_from = egg
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
return do_download()
|
||||||
|
try:
|
||||||
|
pkg_resources.require("setuptools>="+version); return
|
||||||
|
except pkg_resources.VersionConflict, e:
|
||||||
|
if was_imported:
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"The required version of setuptools (>=%s) is not available, and\n"
|
||||||
|
"can't be installed while this script is running. Please install\n"
|
||||||
|
" a more recent version first, using 'easy_install -U setuptools'."
|
||||||
|
"\n\n(Currently using %r)"
|
||||||
|
) % (version, e.args[0])
|
||||||
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||||
|
return do_download()
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
return do_download()
|
||||||
|
|
||||||
|
def download_setuptools(
|
||||||
|
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
|
||||||
|
delay = 15
|
||||||
|
):
|
||||||
|
"""Download setuptools from a specified location and return its filename
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end
|
||||||
|
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||||
|
`delay` is the number of seconds to pause before an actual download attempt.
|
||||||
|
"""
|
||||||
|
import urllib2, shutil
|
||||||
|
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
|
||||||
|
url = download_base + egg_name
|
||||||
|
saveto = os.path.join(to_dir, egg_name)
|
||||||
|
src = dst = None
|
||||||
|
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||||
|
try:
|
||||||
|
from distutils import log
|
||||||
|
if delay:
|
||||||
|
log.warn("""
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
This script requires setuptools version %s to run (even to display
|
||||||
|
help). I will attempt to download it for you (from
|
||||||
|
%s), but
|
||||||
|
you may need to enable firewall access for this script first.
|
||||||
|
I will start the download in %d seconds.
|
||||||
|
|
||||||
|
(Note: if this machine does not have network access, please obtain the file
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
and place it in this directory before rerunning this script.)
|
||||||
|
---------------------------------------------------------------------------""",
|
||||||
|
version, download_base, delay, url
|
||||||
|
); from time import sleep; sleep(delay)
|
||||||
|
log.warn("Downloading %s", url)
|
||||||
|
src = urllib2.urlopen(url)
|
||||||
|
# Read/write all in one block, so we don't create a corrupt file
|
||||||
|
# if the download is interrupted.
|
||||||
|
data = _validate_md5(egg_name, src.read())
|
||||||
|
dst = open(saveto,"wb"); dst.write(data)
|
||||||
|
finally:
|
||||||
|
if src: src.close()
|
||||||
|
if dst: dst.close()
|
||||||
|
return os.path.realpath(saveto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv, version=DEFAULT_VERSION):
|
||||||
|
"""Install or upgrade setuptools and EasyInstall"""
|
||||||
|
try:
|
||||||
|
import setuptools
|
||||||
|
except ImportError:
|
||||||
|
egg = None
|
||||||
|
try:
|
||||||
|
egg = download_setuptools(version, delay=0)
|
||||||
|
sys.path.insert(0,egg)
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
return main(list(argv)+[egg]) # we're done here
|
||||||
|
finally:
|
||||||
|
if egg and os.path.exists(egg):
|
||||||
|
os.unlink(egg)
|
||||||
|
else:
|
||||||
|
if setuptools.__version__ == '0.0.1':
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"You have an obsolete version of setuptools installed. Please\n"
|
||||||
|
"remove it from your system entirely before rerunning this script."
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
req = "setuptools>="+version
|
||||||
|
import pkg_resources
|
||||||
|
try:
|
||||||
|
pkg_resources.require(req)
|
||||||
|
except pkg_resources.VersionConflict:
|
||||||
|
try:
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
except ImportError:
|
||||||
|
from easy_install import main
|
||||||
|
main(list(argv)+[download_setuptools(delay=0)])
|
||||||
|
sys.exit(0) # try to force an exit
|
||||||
|
else:
|
||||||
|
if argv:
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
main(argv)
|
||||||
|
else:
|
||||||
|
print "Setuptools version",version,"or greater has been installed."
|
||||||
|
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
|
||||||
|
|
||||||
|
def update_md5(filenames):
|
||||||
|
"""Update our built-in md5 registry"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
for name in filenames:
|
||||||
|
base = os.path.basename(name)
|
||||||
|
f = open(name,'rb')
|
||||||
|
md5_data[base] = md5(f.read()).hexdigest()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
data = [" %r: %r,\n" % it for it in md5_data.items()]
|
||||||
|
data.sort()
|
||||||
|
repl = "".join(data)
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
srcfile = inspect.getsourcefile(sys.modules[__name__])
|
||||||
|
f = open(srcfile, 'rb'); src = f.read(); f.close()
|
||||||
|
|
||||||
|
match = re.search("\nmd5_data = {\n([^}]+)}", src)
|
||||||
|
if not match:
|
||||||
|
print >>sys.stderr, "Internal error!"
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
src = src[:match.start(1)] + repl + src[match.end(1):]
|
||||||
|
f = open(srcfile,'w')
|
||||||
|
f.write(src)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
|
||||||
|
update_md5(sys.argv[2:])
|
||||||
|
else:
|
||||||
|
main(sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["setuptools", "wheel"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
@ -1,14 +0,0 @@
|
|||||||
email-validator
|
|
||||||
Flask
|
|
||||||
Flask-Admin
|
|
||||||
Flask-Login
|
|
||||||
Flask-Mail
|
|
||||||
Flask-SQLAlchemy
|
|
||||||
Flask-Themes2
|
|
||||||
Flask-Uploads
|
|
||||||
Flask-User
|
|
||||||
Flask-WTF
|
|
||||||
passlib
|
|
||||||
py-bcrypt
|
|
||||||
pycrypto
|
|
||||||
pytz
|
|
@ -1,75 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2010-2021 Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This file is part of ScribeEngine. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is free software: you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
|
||||||
# any later version. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine` module sets up and runs ScribeEngine
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from flask import Flask
|
|
||||||
from flask_mail import Mail
|
|
||||||
from flask_themes2 import Themes, packaged_themes_loader, theme_paths_loader, load_themes_from
|
|
||||||
from flask_user import UserManager
|
|
||||||
|
|
||||||
# from scribeengine.admin import admin
|
|
||||||
from scribeengine.config import read_config_from_file
|
|
||||||
from scribeengine.db import db
|
|
||||||
from scribeengine.models import User
|
|
||||||
from scribeengine.views.account import account
|
|
||||||
from scribeengine.views.node import node_views
|
|
||||||
|
|
||||||
|
|
||||||
def _scribeengine_themes_loader(app):
|
|
||||||
"""
|
|
||||||
Loads ScribeEngine's themes
|
|
||||||
"""
|
|
||||||
themes_path = os.path.join(os.path.dirname(__file__), 'themes')
|
|
||||||
return load_themes_from(themes_path)
|
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_file=None):
|
|
||||||
"""
|
|
||||||
Create the application object
|
|
||||||
"""
|
|
||||||
application = Flask('ScribeEngine')
|
|
||||||
# Set up configuration
|
|
||||||
if not config_file:
|
|
||||||
if os.environ.get('SCRIBEENGINE_CONFIG'):
|
|
||||||
config_file = os.environ['SCRIBEENGINE_CONFIG']
|
|
||||||
elif os.path.exists('config.ini'):
|
|
||||||
config_file = 'config.ini'
|
|
||||||
if config_file:
|
|
||||||
application.config.update(read_config_from_file(config_file))
|
|
||||||
# Set up mail, themes
|
|
||||||
Mail(application)
|
|
||||||
Themes(application, app_identifier='ScribeEngine', loaders=[
|
|
||||||
_scribeengine_themes_loader, packaged_themes_loader, theme_paths_loader])
|
|
||||||
# Set up database
|
|
||||||
db.init_app(application)
|
|
||||||
db.create_all(app=application)
|
|
||||||
# Setup Flask-User
|
|
||||||
UserManager(application, db, User)
|
|
||||||
# Register all the blueprints
|
|
||||||
application.register_blueprint(admin)
|
|
||||||
application.register_blueprint(node_views)
|
|
||||||
application.register_blueprint(account)
|
|
||||||
# Return the application object
|
|
||||||
return application
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from scribeengine import create_app
|
|
||||||
create_app().run(host='0.0.0.0', port=9080, debug=True)
|
|
@ -1,69 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation; version 2 of the License. #
|
|
||||||
# #
|
|
||||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.config` module contains helper classes for config handling
|
|
||||||
"""
|
|
||||||
from configparser import ConfigParser
|
|
||||||
|
|
||||||
from scribeengine.config.cmd import run_cmd
|
|
||||||
|
|
||||||
BOOLEAN_VALUES = ['yes', 'true', 'on', 'no', 'false', 'off']
|
|
||||||
LIST_OPTIONS = ['THEME_PATHS', 'UPLOAD_PATHS']
|
|
||||||
|
|
||||||
|
|
||||||
def read_config_from_file(filename):
|
|
||||||
"""
|
|
||||||
Read the Flask configuration from a config file
|
|
||||||
"""
|
|
||||||
flask_config = {}
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read(filename)
|
|
||||||
for section in config.sections():
|
|
||||||
for option in config.options(section):
|
|
||||||
# Set up the configuration key
|
|
||||||
if section == 'flask':
|
|
||||||
# Options in the flask section don't need FLASK_*
|
|
||||||
key = option.upper()
|
|
||||||
else:
|
|
||||||
key = '{}_{}'.format(section, option).upper()
|
|
||||||
# Get the value, skip it if it is blank
|
|
||||||
string_value = config.get(section, option)
|
|
||||||
if not string_value:
|
|
||||||
continue
|
|
||||||
# Try to figure out what type it is
|
|
||||||
if string_value.isnumeric() and '.' in string_value:
|
|
||||||
value = config.getfloat(section, option)
|
|
||||||
elif string_value.isnumeric():
|
|
||||||
value = config.getint(section, option)
|
|
||||||
elif string_value.lower() in BOOLEAN_VALUES:
|
|
||||||
value = config.getboolean(section, option)
|
|
||||||
elif key in LIST_OPTIONS:
|
|
||||||
value = [p.strip() for p in string_value.split(',') if p.strip()]
|
|
||||||
else:
|
|
||||||
value = string_value
|
|
||||||
# Save this into our flask config dictionary
|
|
||||||
flask_config[key] = value
|
|
||||||
return flask_config
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_cmd()
|
|
@ -1,3 +0,0 @@
|
|||||||
from scribeengine.config.cmd import run_cmd
|
|
||||||
|
|
||||||
run_cmd()
|
|
@ -1,187 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation; version 2 of the License. #
|
|
||||||
# #
|
|
||||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.config` module contains helper classes for config handling
|
|
||||||
"""
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
from configparser import ConfigParser
|
|
||||||
from random import SystemRandom
|
|
||||||
from string import ascii_letters, digits # , punctuation
|
|
||||||
|
|
||||||
# ALLOWED_CHARS = ascii_letters + digits + punctuation
|
|
||||||
ALLOWED_CHARS = ascii_letters + digits
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_secret(length=32):
|
|
||||||
"""
|
|
||||||
Generate a secret
|
|
||||||
"""
|
|
||||||
return ''.join(SystemRandom().choice(ALLOWED_CHARS) for _ in range(length))
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = ArgumentParser()
|
|
||||||
parser.add_argument('-t', '--db-type', metavar="TYPE",
|
|
||||||
help='The type of database to use. Options are "sqlite", "mysql" or "postgres". '
|
|
||||||
'Defaults to "sqlite"')
|
|
||||||
parser.add_argument('-d', '--db-name', metavar='NAME',
|
|
||||||
help='The name of the database. For "sqlite" this will be the filename. '
|
|
||||||
'Defaults to "scribeengine" or "scribeengine.sqlite"')
|
|
||||||
parser.add_argument('-H', '--db-host', metavar='HOSTNAME',
|
|
||||||
help='The hostname or IP address of the database server. Defaults to "localhost"')
|
|
||||||
parser.add_argument('-p', '--db-port', metavar='PORT', type=int,
|
|
||||||
help='The database server port. Defaults to 3306 for "mysql" and 5432 for "postgres"')
|
|
||||||
parser.add_argument('-c', '--config', metavar='FILENAME',
|
|
||||||
help='The filename to write the configuration to. Defaults to "config.ini"')
|
|
||||||
parser.add_argument('-n', '--non-interactive', action='store_true',
|
|
||||||
help='Run non-interactively. Defaults will be used for arguments not supplied')
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def write_config_file(config, filename):
|
|
||||||
"""
|
|
||||||
Write the generated config to a file
|
|
||||||
"""
|
|
||||||
parser = ConfigParser()
|
|
||||||
for section, options in config.items():
|
|
||||||
if not parser.has_section(section):
|
|
||||||
parser.add_section(section)
|
|
||||||
for option, value in options.items():
|
|
||||||
parser.set(section, option, str(value))
|
|
||||||
with open(filename, "w") as config_file:
|
|
||||||
parser.write(config_file)
|
|
||||||
|
|
||||||
|
|
||||||
def prompt(question, options=None, default=None, type_=str):
|
|
||||||
"""
|
|
||||||
Prompt the user for an answer, returning the default value if none is given.
|
|
||||||
"""
|
|
||||||
if options:
|
|
||||||
question = '{question} ({options})'.format(question=question, options='/'.join(options))
|
|
||||||
if default:
|
|
||||||
question = '{question} [{default}]'.format(question=question, default=default)
|
|
||||||
answer = input('{question}: '.format(question=question)).strip()
|
|
||||||
if not answer:
|
|
||||||
return default
|
|
||||||
elif not isinstance(answer, type_):
|
|
||||||
answer = type_(answer)
|
|
||||||
return answer
|
|
||||||
|
|
||||||
|
|
||||||
def build_db_url(args):
|
|
||||||
"""
|
|
||||||
Build an SQLAlchemy connection string using the values given, using defaults for omitted values
|
|
||||||
|
|
||||||
:param db_type: The type of database, "sqlite", "mysql", "postgres"
|
|
||||||
:param db_name: The name of the database
|
|
||||||
:param db_user: The user to connect to the database
|
|
||||||
:param db_password: The password used to authenticate the user
|
|
||||||
:param db_host: The hostname or IP address of the database server
|
|
||||||
:param db_port: The port of the database server
|
|
||||||
|
|
||||||
:return: An SQLAlchemy connection string
|
|
||||||
"""
|
|
||||||
if not args.get('db_type'):
|
|
||||||
args['db_type'] = 'sqlite'
|
|
||||||
if not args.get('db_name'):
|
|
||||||
if args['db_type'] == 'sqlite':
|
|
||||||
args['db_name'] = 'scribeengine.sqlite'
|
|
||||||
args['db_name'] = 'scribeengine'
|
|
||||||
if not args.get('db_user'):
|
|
||||||
args['db_user'] = 'scribeegine'
|
|
||||||
if not args.get('db_password'):
|
|
||||||
args['db_password'] = _generate_secret(40)
|
|
||||||
if not args.get('db_host'):
|
|
||||||
args['db_host'] = 'localhost'
|
|
||||||
if not args.get('db_port'):
|
|
||||||
if args['db_type'] == 'mysql':
|
|
||||||
args['db_port'] = 3306
|
|
||||||
elif args['db_type'] == 'postgres':
|
|
||||||
args['db_port'] = 5432
|
|
||||||
else:
|
|
||||||
args['db_port'] = 0
|
|
||||||
if args['db_type'] == 'sqlite':
|
|
||||||
return '{db_type}:///{db_name}'.format(**args)
|
|
||||||
return '{db_type}://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}'.format(**args)
|
|
||||||
|
|
||||||
|
|
||||||
def ask_db_questions(args):
|
|
||||||
"""
|
|
||||||
Ask questions about the database in order to build a SQLAlchemy connection string
|
|
||||||
|
|
||||||
:param db_type: The type of database, "sqlite", "mysql", "postgres"
|
|
||||||
:param db_name: The name of the database
|
|
||||||
:param db_user: The user to connect to the database
|
|
||||||
:param db_password: The password used to authenticate the user
|
|
||||||
:param db_host: The hostname or IP address of the database server
|
|
||||||
:param db_port: The port of the database server
|
|
||||||
|
|
||||||
:return: An SQLAlchemy connection string
|
|
||||||
"""
|
|
||||||
if not args.get('db_type'):
|
|
||||||
args['db_type'] = prompt('Database type', ['sqlite', 'mysql', 'postgres'], 'sqlite').lower()
|
|
||||||
if not args.get('db_name'):
|
|
||||||
if args.get('db_type') == 'sqlite':
|
|
||||||
args['db_name'] = prompt('Database filename', default='scribeengine.sqlite')
|
|
||||||
args['db_name'] = prompt('Database name', default='scribeengine')
|
|
||||||
if not args.get('db_user'):
|
|
||||||
args['db_user'] = prompt('Database user', default='scribeengine')
|
|
||||||
if not args.get('db_password'):
|
|
||||||
args['db_password'] = prompt('Database password', default=_generate_secret(40))
|
|
||||||
if not args.get('db_host'):
|
|
||||||
args['db_host'] = prompt('Database host', default='localhost')
|
|
||||||
if not args.get('db_port'):
|
|
||||||
if args.get('db_type') in ['mysql', 'mariadb']:
|
|
||||||
args['db_port'] = prompt('Database port', default=3306, type_=int)
|
|
||||||
elif args.get('db_type') == 'postgres':
|
|
||||||
args['db_port'] = prompt('Database port', default=5432, type_=int)
|
|
||||||
else:
|
|
||||||
args['db_port'] = prompt('Database port', default=0, type_=int)
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd():
|
|
||||||
"""
|
|
||||||
Ask the user a set of questions, and then write them to a config file.
|
|
||||||
"""
|
|
||||||
args = vars(parse_args())
|
|
||||||
if not args.pop('non_interactive', False):
|
|
||||||
ask_db_questions(args)
|
|
||||||
config_file = args.pop('config') or 'config.ini'
|
|
||||||
db_url = build_db_url(args)
|
|
||||||
config = {
|
|
||||||
'flask': {
|
|
||||||
'secret_key': _generate_secret()
|
|
||||||
},
|
|
||||||
'sqlalchemy': {
|
|
||||||
'database_uri': db_url,
|
|
||||||
'track_modifications': False
|
|
||||||
},
|
|
||||||
'mail': {
|
|
||||||
|
|
||||||
},
|
|
||||||
'paths': {
|
|
||||||
'theme_path': '',
|
|
||||||
'uploads_path': ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_config_file(config, config_file)
|
|
88
scribeengine/config/deployment.ini_tmpl
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#
|
||||||
|
# ScribeEngine - Pylons configuration
|
||||||
|
#
|
||||||
|
# The %(here)s variable will be replaced with the parent directory of this file
|
||||||
|
#
|
||||||
|
[DEFAULT]
|
||||||
|
debug = true
|
||||||
|
email_to = you@yourdomain.com
|
||||||
|
smtp_server = localhost
|
||||||
|
error_email_from = paste@localhost
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:Paste#http
|
||||||
|
host = 0.0.0.0
|
||||||
|
port = 5000
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = egg:ScribeEngine
|
||||||
|
full_stack = true
|
||||||
|
static_files = true
|
||||||
|
|
||||||
|
cache_dir = %(here)s/data
|
||||||
|
beaker.session.key = scribeengine
|
||||||
|
beaker.session.secret = ${app_instance_secret}
|
||||||
|
beaker.session.timeout = 1209600
|
||||||
|
app_instance_uuid = ${app_instance_uuid}
|
||||||
|
|
||||||
|
# If you'd like to fine-tune the individual locations of the cache data dirs
|
||||||
|
# for the Cache data, or the Session saves, un-comment the desired settings
|
||||||
|
# here:
|
||||||
|
#beaker.cache.data_dir = %(here)s/data/cache
|
||||||
|
#beaker.session.data_dir = %(here)s/data/sessions
|
||||||
|
|
||||||
|
# SQLAlchemy database URL
|
||||||
|
sqlalchemy.url = sqlite:///production.db
|
||||||
|
|
||||||
|
# Media upload directory
|
||||||
|
paths.media = %(here)s/media
|
||||||
|
# Themes directory
|
||||||
|
paths.themes = %(here)s/themes
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
security.salt = secretsalt
|
||||||
|
|
||||||
|
# Mail server settings
|
||||||
|
mail.on = false
|
||||||
|
mail.manager = immediate
|
||||||
|
mail.transport = smtp
|
||||||
|
mail.smtp.server = mail.mydomain.com
|
||||||
|
mail.smtp.username = mymailusername
|
||||||
|
mail.smtp.password = mymailpassword
|
||||||
|
|
||||||
|
# Server-related settings
|
||||||
|
server.timezone = 'Africa/Johannesburg'
|
||||||
|
|
||||||
|
# Setup options (for when you run "paster setup-app config.ini")
|
||||||
|
setup.create_tables = true
|
||||||
|
setup.create_user = false
|
||||||
|
setup.blog_details = false
|
||||||
|
|
||||||
|
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
|
||||||
|
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
|
||||||
|
# execute malicious code after an exception is raised.
|
||||||
|
set debug = false
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = INFO
|
||||||
|
handlers = console
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s
|
64
scribeengine/config/environment.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""Pylons environment configuration"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
from mako.lookup import TemplateLookup
|
||||||
|
from pylons import config
|
||||||
|
from pylons.error import handle_mako_error
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
|
||||||
|
from scribeengine.lib import app_globals
|
||||||
|
from scribeengine.lib import helpers
|
||||||
|
from scribeengine.config.routing import make_map
|
||||||
|
from scribeengine.model import init_model, Variable
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
def load_environment(global_conf, app_conf):
|
||||||
|
"""Configure the Pylons environment via the ``pylons.config``
|
||||||
|
object
|
||||||
|
"""
|
||||||
|
# Setup the SQLAlchemy database engine
|
||||||
|
engine = engine_from_config(app_conf, 'sqlalchemy.')
|
||||||
|
init_model(engine)
|
||||||
|
|
||||||
|
theme_name = None
|
||||||
|
if not app_conf.get('setup-app'):
|
||||||
|
# Pull out theme variable
|
||||||
|
theme = Session.query(Variable).get(u'theme')
|
||||||
|
if theme:
|
||||||
|
theme_name = theme.value
|
||||||
|
|
||||||
|
# Set up Pylons paths
|
||||||
|
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
template_paths = []
|
||||||
|
static_paths = []
|
||||||
|
if theme_name:
|
||||||
|
theme_dir = os.path.join(app_conf[u'paths.themes'], theme_name)
|
||||||
|
template_paths.append(os.path.join(theme_dir, 'templates'))
|
||||||
|
static_paths.append(os.path.join(theme_dir, 'public'))
|
||||||
|
template_paths.append(os.path.join(root, 'templates'))
|
||||||
|
static_paths.append(os.path.join(root, 'public'))
|
||||||
|
|
||||||
|
paths = dict(
|
||||||
|
root=root,
|
||||||
|
controllers=os.path.join(root, 'controllers'),
|
||||||
|
static_files=static_paths,
|
||||||
|
templates=template_paths
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize config with the basic options
|
||||||
|
config.init_app(global_conf, app_conf, package='scribeengine', paths=paths)
|
||||||
|
|
||||||
|
config['routes.map'] = make_map()
|
||||||
|
config['pylons.app_globals'] = app_globals.Globals()
|
||||||
|
config['pylons.h'] = helpers
|
||||||
|
|
||||||
|
# Create the Mako TemplateLookup, with the default auto-escaping
|
||||||
|
config['pylons.app_globals'].mako_lookup = TemplateLookup(
|
||||||
|
directories=paths['templates'],
|
||||||
|
error_handler=handle_mako_error,
|
||||||
|
module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
|
||||||
|
input_encoding='utf-8', default_filters=['escape'],
|
||||||
|
imports=['from webhelpers.html import escape'])
|
||||||
|
|
||||||
|
# CONFIGURATION OPTIONS HERE (note: all config options will override
|
||||||
|
# any Pylons config options)
|
70
scribeengine/config/middleware.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
"""Pylons middleware initialization"""
|
||||||
|
from beaker.middleware import CacheMiddleware, SessionMiddleware
|
||||||
|
from paste.cascade import Cascade
|
||||||
|
from paste.registry import RegistryManager
|
||||||
|
from paste.urlparser import StaticURLParser
|
||||||
|
from paste.deploy.converters import asbool
|
||||||
|
from pylons import config
|
||||||
|
from pylons.middleware import ErrorHandler, StatusCodeRedirect
|
||||||
|
from pylons.wsgiapp import PylonsApp
|
||||||
|
from routes.middleware import RoutesMiddleware
|
||||||
|
|
||||||
|
from scribeengine.config.environment import load_environment
|
||||||
|
|
||||||
|
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
|
||||||
|
"""Create a Pylons WSGI application and return it
|
||||||
|
|
||||||
|
``global_conf``
|
||||||
|
The inherited configuration for this application. Normally from
|
||||||
|
the [DEFAULT] section of the Paste ini file.
|
||||||
|
|
||||||
|
``full_stack``
|
||||||
|
Whether this application provides a full WSGI stack (by default,
|
||||||
|
meaning it handles its own exceptions and errors). Disable
|
||||||
|
full_stack when this application is "managed" by another WSGI
|
||||||
|
middleware.
|
||||||
|
|
||||||
|
``static_files``
|
||||||
|
Whether this application serves its own static files; disable
|
||||||
|
when another web server is responsible for serving them.
|
||||||
|
|
||||||
|
``app_conf``
|
||||||
|
The application's local configuration. Normally specified in
|
||||||
|
the [app:<name>] section of the Paste ini file (where <name>
|
||||||
|
defaults to main).
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Configure the Pylons environment
|
||||||
|
load_environment(global_conf, app_conf)
|
||||||
|
|
||||||
|
# The Pylons WSGI app
|
||||||
|
app = PylonsApp()
|
||||||
|
|
||||||
|
# Routing/Session/Cache Middleware
|
||||||
|
app = RoutesMiddleware(app, config['routes.map'])
|
||||||
|
app = SessionMiddleware(app, config)
|
||||||
|
app = CacheMiddleware(app, config)
|
||||||
|
|
||||||
|
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
|
||||||
|
|
||||||
|
if asbool(full_stack):
|
||||||
|
# Handle Python exceptions
|
||||||
|
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
|
||||||
|
|
||||||
|
# Display error documents for 401, 403, 404 status codes (and
|
||||||
|
# 500 when debug is disabled)
|
||||||
|
if asbool(config['debug']):
|
||||||
|
app = StatusCodeRedirect(app)
|
||||||
|
else:
|
||||||
|
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
|
||||||
|
|
||||||
|
# Establish the Registry for this application
|
||||||
|
app = RegistryManager(app)
|
||||||
|
|
||||||
|
if asbool(static_files):
|
||||||
|
# Serve static files
|
||||||
|
static_apps = [StaticURLParser(path)
|
||||||
|
for path in config['pylons.paths']['static_files']]
|
||||||
|
app = Cascade(static_apps + [app])
|
||||||
|
|
||||||
|
return app
|
62
scribeengine/config/routing.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Routes configuration
|
||||||
|
|
||||||
|
The more specific and detailed routes should be defined first so they
|
||||||
|
may take precedent over the more generic routes. For more information
|
||||||
|
refer to the routes manual at http://routes.groovie.org/docs/
|
||||||
|
"""
|
||||||
|
from pylons import config
|
||||||
|
from routes import Mapper
|
||||||
|
|
||||||
|
def make_map():
|
||||||
|
"""Create, configure and return the routes Mapper"""
|
||||||
|
map = Mapper(directory=config['pylons.paths']['controllers'],
|
||||||
|
always_scan=config['debug'])
|
||||||
|
map.minimization = False
|
||||||
|
|
||||||
|
# The ErrorController route (handles 404/500 error pages); it should
|
||||||
|
# likely stay at the top, ensuring it can always be resolved
|
||||||
|
map.connect('/error/{action}', controller='error')
|
||||||
|
map.connect('/error/{action}/{id}', controller='error')
|
||||||
|
|
||||||
|
# CUSTOM ROUTES HERE
|
||||||
|
|
||||||
|
map.connect('/archive/{year}', controller='blog', action='archive')
|
||||||
|
map.connect('/archive/{year}/{month}', controller='blog', action='archive')
|
||||||
|
map.connect('/archive/{year}/{month}/{day}', controller='blog', action='archive')
|
||||||
|
map.connect('/archive/{year}/{month}/{day}/{url}', controller='blog', action='view')
|
||||||
|
map.connect('/search', controller='blog', action='search')
|
||||||
|
map.connect('/tag/{id}', controller='blog', action='tag')
|
||||||
|
map.connect('/calendar/{year}/{month}', controller='blog', action='calendar')
|
||||||
|
|
||||||
|
map.connect('/{controller}')
|
||||||
|
map.connect('/{controller}/{action}')
|
||||||
|
map.connect('/{controller}/{action}/{id}')
|
||||||
|
|
||||||
|
map.connect('/{url}', controller='page', action='view')
|
||||||
|
|
||||||
|
map.connect('/', controller='blog', action='index')
|
||||||
|
|
||||||
|
return map
|
342
scribeengine/controllers/account.py
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
from urlparse import urlsplit
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from formencode.validators import Int
|
||||||
|
from pytz import all_timezones
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.lib.validation.client import JSString, JSEmail
|
||||||
|
from scribeengine.lib.validation.server import UnicodeString, Email, FieldsMatch
|
||||||
|
from scribeengine.lib import utils
|
||||||
|
from scribeengine.model import User
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AccountController(BaseController):
|
||||||
|
|
||||||
|
@authenticate()
|
||||||
|
def index(self):
|
||||||
|
c.page_title = u'My Account'
|
||||||
|
c.timezones = all_timezones
|
||||||
|
return render(u'/account/index.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-account')
|
||||||
|
def index_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'account-nick': JSString(required=True,
|
||||||
|
message=u'You need to type in a nick.'),
|
||||||
|
u'account-email': JSEmail(required=True,
|
||||||
|
message=u'You need to supply a valid e-mail address.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def index_schema(self):
|
||||||
|
return {
|
||||||
|
'account-nick': UnicodeString(not_empty=True,
|
||||||
|
messages={u'empty': u'You need to type in a nick.'}),
|
||||||
|
'account-email': Email(not_empty=True,
|
||||||
|
messages={u'empty': u'You need to supply a valid e-mail address.'})
|
||||||
|
}
|
||||||
|
|
||||||
|
def index_POST(self):
|
||||||
|
try:
|
||||||
|
for key, value in c.form_values.iteritems():
|
||||||
|
setattr(c.current_user, key[8:], value)
|
||||||
|
Session.add(c.current_user)
|
||||||
|
Session.commit()
|
||||||
|
h.flash.set_message(u'Successfully updated your account.', u'success')
|
||||||
|
except:
|
||||||
|
h.flash.set_message(u'There was a problem updating your account.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account'))
|
||||||
|
|
||||||
|
@authenticate()
|
||||||
|
def password(self):
|
||||||
|
c.page_title = u'Your Password'
|
||||||
|
return render(u'/account/password.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-password')
|
||||||
|
def password_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'password-password': JSString(required=True, message=u'You haven\'t typed in a password.'),
|
||||||
|
u'password-confirm': JSString(required=True, equalTo=u'#password-password', message=u'Your passwords don\'t match.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def password_schema(self):
|
||||||
|
return {
|
||||||
|
'password-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}),
|
||||||
|
'confirm-password': [FieldsMatch('password-password', 'password-confirm', messages={'invalid': u'Your passwords don\'t match.'})]
|
||||||
|
}
|
||||||
|
|
||||||
|
@authenticate()
|
||||||
|
def password_POST(self):
|
||||||
|
password_hash = utils.hash_password(c.form_values[u'password-password'])
|
||||||
|
log.debug('Old Hash: "%s"', c.current_user.password)
|
||||||
|
log.debug('New Hash: "%s"', password_hash)
|
||||||
|
c.current_user.password = password_hash
|
||||||
|
c.current_user.modified = datetime.now()
|
||||||
|
Session.add(c.current_user)
|
||||||
|
Session.commit()
|
||||||
|
h.flash.set_message(u'Successfully updated your password.', u'success')
|
||||||
|
h.redirect_to('/account/password')
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
c.page_title = u'Register'
|
||||||
|
return render(u'/account/register.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-register')
|
||||||
|
def register_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'register-email': JSEmail(required=True,
|
||||||
|
message=u'You haven\'t typed in an e-mail address.'),
|
||||||
|
u'register-password': JSString(required=True,
|
||||||
|
message=u'You haven\'t typed in a password.'),
|
||||||
|
u'register-confirm': JSString(required=True, equalTo=u'#password',
|
||||||
|
message=u'Your passwords don\'t match.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def register_schema(self):
|
||||||
|
return {
|
||||||
|
'register-email': Email(not_empty=True,
|
||||||
|
messages={'empty': u'You haven\'t typed in an e-mail address.'}),
|
||||||
|
'register-password': UnicodeString(not_empty=True,
|
||||||
|
messages={'empty': u'You haven\'t typed in a password.'}),
|
||||||
|
'confirm-password': [
|
||||||
|
FieldsMatch('register-password', 'register-confirm',
|
||||||
|
messages={'invalid': u'Your passwords don\'t match.'})]
|
||||||
|
}
|
||||||
|
|
||||||
|
def register_POST(self):
|
||||||
|
activation_code = u''.join(random.sample(string.letters + string.digits, 40))
|
||||||
|
user = User(
|
||||||
|
nick=c.form_values[u'register-nick'],
|
||||||
|
email=c.form_values[u'register-email'],
|
||||||
|
password=utils.hash_password(c.form_values[u'register-password']),
|
||||||
|
activation_key=activation_code
|
||||||
|
)
|
||||||
|
Session.add(user)
|
||||||
|
Session.commit()
|
||||||
|
blog_mail = Session.query(Variable).get(u'blog mail')
|
||||||
|
blog_title = Session.query(Variable).get(u'blog title')
|
||||||
|
blog_host = Session.query(Variable).get(u'blog host')
|
||||||
|
if not blog_host:
|
||||||
|
url = u'%s://%s' % (request.environ[u'wsgi.url_scheme'],
|
||||||
|
request.environ[u'HTTP_HOST'])
|
||||||
|
blog_host = Variable(key=u'blog host', value=url)
|
||||||
|
Session.add(blog_host)
|
||||||
|
Session.commit()
|
||||||
|
utils.send_mail(u'/email/activate.mako', u'%s <%s>' % (user.nick, user.email),
|
||||||
|
u'%s <%s>' % (blog_mail.value, blog_title.value),
|
||||||
|
u'[%s] Activate your account!' % blog_title.value,
|
||||||
|
{
|
||||||
|
'user': user,
|
||||||
|
'blog_title': blog_title.value,
|
||||||
|
'blog_host': blog_host.value
|
||||||
|
})
|
||||||
|
h.flash.set_message(u'An e-mail has been sent to your e-mail address. '
|
||||||
|
u'Please activate your account by clicking on the link in your '
|
||||||
|
u'e-mail.', u'success')
|
||||||
|
h.redirect_to('/')
|
||||||
|
|
||||||
|
def activate(self, id=None):
|
||||||
|
activation_code = request.GET.get('code')
|
||||||
|
if not activation_code:
|
||||||
|
h.flash.set_message(u'Your activation code was missing or '
|
||||||
|
u'incorrect. Please check your activation e-mail.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'register'))
|
||||||
|
if not id:
|
||||||
|
h.flash.set_message(u'Your username was missing or incorrect. '
|
||||||
|
u'Please check your activation e-mail.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'register'))
|
||||||
|
user = Session.query(User)\
|
||||||
|
.filter_by(id=id)\
|
||||||
|
.filter_by(activation_key=activation_code)\
|
||||||
|
.first()
|
||||||
|
if not user:
|
||||||
|
h.flash.set_message(u'Your username or activation code is '
|
||||||
|
u'incorrect. Please check your activation e-mail.', u'error')
|
||||||
|
h.redirect_to(h.url_for(action=u'register'))
|
||||||
|
user.activation_key = None
|
||||||
|
user.modified = datetime.now()
|
||||||
|
Session.add(user)
|
||||||
|
Session.commit()
|
||||||
|
h.flash.set_message(u'Your account has been activated! Please log in '
|
||||||
|
u'with your e-mail address and the password you typed in during '
|
||||||
|
u'registration.', u'success')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
c.page_title = u'Reset Password'
|
||||||
|
return render(u'/account/reset.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-reset')
|
||||||
|
def reset_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'reset-email': JSEmail(required=True, message=u'You haven\'t typed in a valid e-mail address.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def reset_schema(self):
|
||||||
|
return {
|
||||||
|
'reset-email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in a valid e-mail address.'})
|
||||||
|
}
|
||||||
|
|
||||||
|
def reset_POST(self):
|
||||||
|
email = c.form_values[u'reset-email']
|
||||||
|
user = Session.query(User).filter_by(email=email).first()
|
||||||
|
if not user:
|
||||||
|
h.flash.set_message(u'Your e-mail address is not in the system.', u'error')
|
||||||
|
else:
|
||||||
|
activation_code = u''.join(random.sample(string.letters + string.digits, 40))
|
||||||
|
user.activation_key = activation_code
|
||||||
|
user.modified = datetime.now()
|
||||||
|
Session.add(user)
|
||||||
|
Session.commit()
|
||||||
|
blog_mail = Session.query(Variable).get(u'blog mail')
|
||||||
|
blog_title = Session.query(Variable).get(u'blog title')
|
||||||
|
blog_host = Session.query(Variable).get(u'blog host')
|
||||||
|
if not blog_host:
|
||||||
|
url = u'%s://%s' % (request.environ[u'wsgi.url_scheme'],
|
||||||
|
request.environ[u'HTTP_HOST'])
|
||||||
|
blog_host = Variable(key=u'blog host', value=url)
|
||||||
|
Session.add(blog_host)
|
||||||
|
Session.commit()
|
||||||
|
utils.send_mail(u'/email/reset.mako', u'%s <%s>' % (user.nick, user.email),
|
||||||
|
u'%s <%s>' % (blog_mail.value, blog_title.value),
|
||||||
|
u'[%s] Reset your password!' % blog_title.value,
|
||||||
|
{
|
||||||
|
'user': user,
|
||||||
|
'blog_title': blog_title.value,
|
||||||
|
'blog_host': blog_host.value
|
||||||
|
})
|
||||||
|
h.flash.set_message(u'An e-mail has been sent to your e-mail address. '
|
||||||
|
u'Please reset your password by clicking on the link in your '
|
||||||
|
u'e-mail.', u'success')
|
||||||
|
h.redirect_to('/account/login')
|
||||||
|
|
||||||
|
def resetpassword(self, id=None):
|
||||||
|
"""
|
||||||
|
Reset your password.
|
||||||
|
"""
|
||||||
|
if not id or not request.GET.get(u'code'):
|
||||||
|
h.flash.set_message(u'There was a problem with your activation code, please reset your password again.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
c.user = Session.query(User).get(id)
|
||||||
|
if not c.user:
|
||||||
|
h.flash.set_message(u'There was a problem with your account, please reset your password again.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
if c.user.activation_key != request.GET.get(u'code'):
|
||||||
|
h.flash.set_message(u'There was a problem with your activation code, please reset your password again.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
c.page_title = u'Reset Password'
|
||||||
|
return render(u'/account/resetpassword.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-resetpassword')
|
||||||
|
def resetpassword_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'password-password': JSString(required=True, message=u'You haven\'t typed in a password.'),
|
||||||
|
u'password-confirm': JSString(required=True, equalTo=u'#password-password', message=u'Your passwords don\'t match.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def resetpassword_schema(self):
|
||||||
|
return {
|
||||||
|
'password-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}),
|
||||||
|
'confirm-password': [FieldsMatch('password-password', 'password-confirm', messages={'invalid': u'Your passwords don\'t match.'})]
|
||||||
|
}
|
||||||
|
|
||||||
|
def resetpassword_POST(self, id=None):
|
||||||
|
user = Session.query(User).get(id)
|
||||||
|
if not user:
|
||||||
|
h.flash.set_message(u'There was a problem with your account, '
|
||||||
|
u'please reset your password again.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
user.password = utils.hash_password(c.form_values[u'password-password'])
|
||||||
|
user.activation_key = None
|
||||||
|
user.modified = datetime.now()
|
||||||
|
Session.add(user)
|
||||||
|
Session.commit()
|
||||||
|
h.flash.set_message(u'Successfully updated your password. Please login '
|
||||||
|
u'with your new password.', u'success')
|
||||||
|
h.redirect_to('/account/login')
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
if u'redirect_url' not in session and u'REFERER' in request.headers:
|
||||||
|
session[u'redirect_url'] = str(urlsplit(request.headers[u'REFERER']).path)
|
||||||
|
session.save()
|
||||||
|
c.page_title = u'Login'
|
||||||
|
return render(u'/account/login.mako')
|
||||||
|
|
||||||
|
@jsvalidate(u'account-login')
|
||||||
|
def login_jsschema(self):
|
||||||
|
return {
|
||||||
|
u'login-email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'),
|
||||||
|
u'login-password': JSString(required=True, message=u'You haven\'t typed in a password.')
|
||||||
|
}
|
||||||
|
|
||||||
|
def login_schema(self):
|
||||||
|
return {
|
||||||
|
'login-email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}),
|
||||||
|
'login-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'})
|
||||||
|
}
|
||||||
|
|
||||||
|
def login_POST(self):
|
||||||
|
log.debug('Logging in as "%s" with password "%s"', c.form_values[u'login-email'], c.form_values[u'login-password'])
|
||||||
|
user = Session.query(User).filter_by(email=c.form_values[u'login-email']).first()
|
||||||
|
password = utils.hash_password(c.form_values[u'login-password'])
|
||||||
|
if not user or user.password != password:
|
||||||
|
log.debug('Username or password are incorrect.')
|
||||||
|
h.flash.set_message(u'Your username or password are incorrect.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
elif user and user.activation_key is not None:
|
||||||
|
log.debug('Unactivated account.')
|
||||||
|
h.flash.set_message(u'Your account has not yet been activated. Please check your e-mail for a link to activate your account.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
elif user and user.password == password:
|
||||||
|
log.debug('Logged in successfully.')
|
||||||
|
redirect_url = str(session.get(u'redirect_url', u'/'))
|
||||||
|
if u'redirect_url' in session:
|
||||||
|
del session[u'redirect_url']
|
||||||
|
else:
|
||||||
|
redirect_url = '/'
|
||||||
|
if redirect_url == '/account/login':
|
||||||
|
redirect_url = '/'
|
||||||
|
session[u'REMOTE_USER'] = user.id
|
||||||
|
session.save()
|
||||||
|
h.flash.set_message(u'You have logged in successfully.', u'success')
|
||||||
|
h.redirect_to(redirect_url)
|
||||||
|
else:
|
||||||
|
log.debug('"user" is None.')
|
||||||
|
del session[u'REMOTE_USER']
|
||||||
|
session.save()
|
||||||
|
h.flash.set_message(u'There was a problem logging you in.', u'error')
|
||||||
|
h.redirect_to(h.url_for(controller=u'account', action=u'login'))
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
del session[u'REMOTE_USER']
|
||||||
|
session.save()
|
||||||
|
h.flash.set_message(u'You have logged out successfully.', u'success')
|
||||||
|
h.redirect_to('/')
|
||||||
|
|
192
scribeengine/controllers/blog.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from pprint import pformat
|
||||||
|
|
||||||
|
from sqlalchemy.sql import or_
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.lib import utils
|
||||||
|
from scribeengine.model import Post, Comment, Tag
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class BlogController(BaseController):
|
||||||
|
|
||||||
|
def __before__(self):
|
||||||
|
BaseController.__before__(self)
|
||||||
|
self._add_javascript(u'jquery.elastic.js')
|
||||||
|
self._add_javascript(u'ScribeEngine.Blog.js')
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
posts = Session.query(Post)\
|
||||||
|
.filter_by(status=u'published')\
|
||||||
|
.order_by(Post.created.desc())
|
||||||
|
pagination = utils.paginate(posts, 10,
|
||||||
|
int(request.GET.get(u'page', 1)), '/')
|
||||||
|
c.posts = pagination[u'records']
|
||||||
|
if pagination[u'prev'] != pagination[u'page']:
|
||||||
|
c.first_page = pagination[u'first']
|
||||||
|
c.prev_page = pagination[u'prev']
|
||||||
|
if pagination[u'next'] != pagination[u'page']:
|
||||||
|
c.next_page = pagination[u'next']
|
||||||
|
c.last_page = pagination[u'last']
|
||||||
|
c.list_start = pagination[u'start']
|
||||||
|
c.list_total = pagination[u'total']
|
||||||
|
c.list_end = pagination[u'end']
|
||||||
|
return render(u'/blog/index.mako')
|
||||||
|
|
||||||
|
def archive(self, year=None, month=None, day=None):
|
||||||
|
if day and month and year:
|
||||||
|
start_date = datetime(int(year), int(month), int(day), 0, 0, 0, 0)
|
||||||
|
end_date = datetime(int(year), int(month), int(day), 23, 59, 59, 99999)
|
||||||
|
c.datestring = start_date.strftime('%d %B %Y')
|
||||||
|
if c.datestring[0] == u'0':
|
||||||
|
c.datestring = c.datestring[1:]
|
||||||
|
elif month and year and not day:
|
||||||
|
start_date = utils.month_first_day(datetime(int(year), int(month), 1))
|
||||||
|
end_date = utils.month_last_day(datetime(int(year), int(month), 1))
|
||||||
|
c.datestring = start_date.strftime('%B %Y')
|
||||||
|
elif year and not month:
|
||||||
|
start_date = datetime(int(year), 1, 1, 0, 0, 0, 0)
|
||||||
|
end_date = datetime(int(year), 12, 31, 23, 59, 59, 99999)
|
||||||
|
c.datestring = start_date.strftime('%Y')
|
||||||
|
else:
|
||||||
|
start_date = None
|
||||||
|
end_date = None
|
||||||
|
c.datestring = u'all time'
|
||||||
|
c.page_title = u'Archive for %s.' % c.datestring
|
||||||
|
posts = Session.query(Post)
|
||||||
|
if start_date and end_date:
|
||||||
|
posts = posts\
|
||||||
|
.filter(Post.created >= start_date)\
|
||||||
|
.filter(Post.created <= end_date)
|
||||||
|
posts = posts.order_by(Post.created.desc())
|
||||||
|
pagination = utils.paginate(posts, 10,
|
||||||
|
int(request.GET.get(u'page', 1)), '/')
|
||||||
|
c.posts = pagination[u'records']
|
||||||
|
if pagination[u'prev'] != pagination[u'page']:
|
||||||
|
c.first_page = pagination[u'first']
|
||||||
|
c.prev_page = pagination[u'prev']
|
||||||
|
if pagination[u'next'] != pagination[u'page']:
|
||||||
|
c.next_page = pagination[u'next']
|
||||||
|
c.last_page = pagination[u'last']
|
||||||
|
c.list_start = pagination[u'start']
|
||||||
|
c.list_total = pagination[u'total']
|
||||||
|
c.list_end = pagination[u'end']
|
||||||
|
return render(u'/blog/archive.mako')
|
||||||
|
|
||||||
|
def view(self, url):
|
||||||
|
c.post = Session.query(Post)\
|
||||||
|
.filter_by(url=url)\
|
||||||
|
.filter_by(status=u'published')\
|
||||||
|
.first()
|
||||||
|
c.page_title = c.post.title
|
||||||
|
return render(u'/blog/view.mako')
|
||||||
|
|
||||||
|
def tag(self, id=None):
|
||||||
|
if not id:
|
||||||
|
h.redirect_to('/')
|
||||||
|
c.tag = Session.query(Tag).filter_by(url=id).first()
|
||||||
|
if not c.tag:
|
||||||
|
h.redirect_to('/')
|
||||||
|
c.page_title = u'Blog posts with tag: %s' % c.tag.name
|
||||||
|
return render('/blog/tag.mako')
|
||||||
|
|
||||||
|
@authenticate()
|
||||||
|
def comment_POST(self, id):
|
||||||
|
if not id:
|
||||||
|
h.flash.set_message(u'There was a problem submitting your comment.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
post = Session.query(Post).get(id)
|
||||||
|
if not post or post.comment_status != u'open':
|
||||||
|
h.flash.set_message(u'There was a problem submitting your comment.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
comment = Comment(
|
||||||
|
user = c.current_user,
|
||||||
|
title = c.form_values[u'title'],
|
||||||
|
body = c.form_values[u'body']
|
||||||
|
)
|
||||||
|
post.comments.append(comment)
|
||||||
|
Session.add(post)
|
||||||
|
Session.commit()
|
||||||
|
h.flash.set_message(u'Successfully submitted your comment.', u'success')
|
||||||
|
h.redirect_to(h.url_for_post(post))
|
||||||
|
|
||||||
|
def search(self):
|
||||||
|
c.querystring = request.GET.get(u'q')
|
||||||
|
if not c.querystring:
|
||||||
|
h.flash.set_message(u'You didn\'t supply anything to search for.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
kwprocessor = utils.KeywordProcessor(
|
||||||
|
groups=[None, '+', '-'],
|
||||||
|
group=tuple,
|
||||||
|
normalize=lambda s: s.strip(' \"\'')
|
||||||
|
)
|
||||||
|
keywords, ands, nots = kwprocessor.split(c.querystring)
|
||||||
|
or_clauses = []
|
||||||
|
for kw in keywords:
|
||||||
|
or_clauses.append(Post.body.contains(kw.strip()))
|
||||||
|
or_clauses.append(Post.title.contains(kw.strip()))
|
||||||
|
and_clauses = [or_(Post.body.contains(aw.strip()),
|
||||||
|
Post.title.contains(aw.strip())) for aw in ands]
|
||||||
|
not_clauses = [or_(Post.body.contains(nw.strip()),
|
||||||
|
Post.title.contains(nw.strip())) for nw in nots]
|
||||||
|
c.posts = Session.query(Post)
|
||||||
|
if len(or_clauses) > 0:
|
||||||
|
c.posts = c.posts.filter(or_(*or_clauses))
|
||||||
|
if len(and_clauses) > 0:
|
||||||
|
for and_clause in and_clauses:
|
||||||
|
c.posts = c.posts.filter(and_clause)
|
||||||
|
if len(not_clauses) > 0:
|
||||||
|
for not_clause in not_clauses:
|
||||||
|
c.posts = c.posts.filter(~not_clause)
|
||||||
|
c.posts = c.posts.order_by(Post.created.desc()).all()
|
||||||
|
c.page_title = u'Search'
|
||||||
|
return render(u'/blog/search.mako')
|
||||||
|
|
||||||
|
def calendar(self, year, month):
|
||||||
|
#c.calendar = Calendar(6)
|
||||||
|
#c.today = datetime.today()
|
||||||
|
server_tz = timezone(config.get(u'server.timezone', u'UTC'))
|
||||||
|
user_tz = c.current_user.timezone if c.current_user and c.current_user.timezone else u'UTC'
|
||||||
|
now = datetime.now(server_tz).astimezone(timezone(user_tz))
|
||||||
|
c.thismonth = now.replace(int(year), int(month))
|
||||||
|
c.prev_month = c.thismonth - monthdelta(1)
|
||||||
|
c.next_month = c.thismonth + monthdelta(1)
|
||||||
|
month_start = datetime(c.thismonth.year, c.thismonth.month, 1, 0, 0, 0, 0)
|
||||||
|
month_end = c.next_month.replace(day=1, hour=23, minute=59, second=59, microsecond=9999) - timedelta(seconds=1)
|
||||||
|
posts = Session.query(Post)\
|
||||||
|
.filter(Post.created >= month_start)\
|
||||||
|
.filter(Post.created <= month_end)\
|
||||||
|
.all()
|
||||||
|
c.month_posts = {}
|
||||||
|
for post in posts:
|
||||||
|
if post.created.day not in c.month_posts:
|
||||||
|
c.month_posts[post.created.day] = []
|
||||||
|
c.month_posts[post.created.day].append(post)
|
||||||
|
return render(u'/calendar.mako')
|
||||||
|
|
46
scribeengine/controllers/error.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import cgi
|
||||||
|
|
||||||
|
from paste.urlparser import PkgResourcesParser
|
||||||
|
from pylons import request
|
||||||
|
from pylons.controllers.util import forward
|
||||||
|
from pylons.middleware import error_document_template
|
||||||
|
from webhelpers.html.builder import literal
|
||||||
|
|
||||||
|
from scribeengine.lib.base import BaseController
|
||||||
|
|
||||||
|
class ErrorController(BaseController):
|
||||||
|
|
||||||
|
"""Generates error documents as and when they are required.
|
||||||
|
|
||||||
|
The ErrorDocuments middleware forwards to ErrorController when error
|
||||||
|
related status codes are returned from the application.
|
||||||
|
|
||||||
|
This behaviour can be altered by changing the parameters to the
|
||||||
|
ErrorDocuments middleware in your config/middleware.py file.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def document(self):
|
||||||
|
"""Render the error document"""
|
||||||
|
resp = request.environ.get('pylons.original_response')
|
||||||
|
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
|
||||||
|
page = error_document_template % \
|
||||||
|
dict(prefix=request.environ.get('SCRIPT_NAME', ''),
|
||||||
|
code=cgi.escape(request.GET.get('code', str(resp.status_int))),
|
||||||
|
message=content)
|
||||||
|
return page
|
||||||
|
|
||||||
|
def img(self, id):
|
||||||
|
"""Serve Pylons' stock images"""
|
||||||
|
return self._serve_file('/'.join(['media/img', id]))
|
||||||
|
|
||||||
|
def style(self, id):
|
||||||
|
"""Serve Pylons' stock stylesheets"""
|
||||||
|
return self._serve_file('/'.join(['media/style', id]))
|
||||||
|
|
||||||
|
def _serve_file(self, path):
|
||||||
|
"""Call Paste's FileApp (a WSGI application) to serve the file
|
||||||
|
at the specified path
|
||||||
|
"""
|
||||||
|
request.environ['PATH_INFO'] = '/%s' % path
|
||||||
|
return forward(PkgResourcesParser('pylons', 'pylons'))
|
59
scribeengine/controllers/feed.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
import re
|
||||||
|
|
||||||
|
from feedformatter import Feed
|
||||||
|
import time
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
from scribeengine.model import Post, Variable
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class FeedController(BaseController):
|
||||||
|
|
||||||
|
def _generate_feed(self):
|
||||||
|
blog_title = Session.query(Variable).get(u'blog title').value
|
||||||
|
blog_slogan = Session.query(Variable).get(u'blog slogan').value
|
||||||
|
blog_link = str('%s://%s' % (request.environ[u'wsgi.url_scheme'], \
|
||||||
|
request.environ[u'HTTP_HOST']))
|
||||||
|
if blog_link.endswith(u'/'):
|
||||||
|
blog_link = blog_link[:-1]
|
||||||
|
posts = Session.query(Post)\
|
||||||
|
.filter_by(status=u'published')\
|
||||||
|
.order_by(Post.created.desc())\
|
||||||
|
.all()
|
||||||
|
# Create the feed
|
||||||
|
feed = Feed()
|
||||||
|
# Set the feed/channel level properties
|
||||||
|
feed.feed[u'title'] = blog_title
|
||||||
|
feed.feed[u'link'] = blog_link + u'/'
|
||||||
|
feed.feed[u'description'] = blog_slogan
|
||||||
|
for post in posts:
|
||||||
|
# Create an item
|
||||||
|
item = {}
|
||||||
|
item[u'title'] = post.title
|
||||||
|
item[u'link'] = blog_link + h.url_for_post(post)
|
||||||
|
item[u'author'] = post.user.nick
|
||||||
|
item[u'description'] = re.sub(r'<(.*?)>', u'', h.teaser(post.body))
|
||||||
|
item[u'pubDate'] = post.created.timetuple()
|
||||||
|
item[u'guid'] = str(uuid.uuid5(uuid.NAMESPACE_URL, blog_link + h.url_for_post(post)))
|
||||||
|
# Add item to feed
|
||||||
|
feed.items.append(item)
|
||||||
|
return feed
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
h.redirect_to(h.url_for(action=u'atom'))
|
||||||
|
|
||||||
|
def rss(self, id=u'2.0'):
|
||||||
|
feed = self._generate_feed()
|
||||||
|
if id == u'1.0':
|
||||||
|
return feed.format_rss1_string()
|
||||||
|
else:
|
||||||
|
return feed.format_rss2_string()
|
||||||
|
|
||||||
|
def atom(self):
|
||||||
|
feed = self._generate_feed()
|
||||||
|
return feed.format_atom_string()
|
||||||
|
|
106
scribeengine/controllers/media.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from pprint import pprint, pformat
|
||||||
|
|
||||||
|
from pylons.decorators import jsonify
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.lib import utils
|
||||||
|
from scribeengine.model import MediaType, File
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MediaController(BaseController):
|
||||||
|
|
||||||
|
def __before__(self):
|
||||||
|
BaseController.__before__(self)
|
||||||
|
self._add_javascript(u'tinymce/tiny_mce_popup.js')
|
||||||
|
self._add_javascript(u'ScribeEngine.Media.js')
|
||||||
|
|
||||||
|
def _get_directories(self, parent=None, tree=[]):
|
||||||
|
old_root = parent
|
||||||
|
dirname = os.path.split(parent)[1]
|
||||||
|
node = {u'data': dirname, u'state': u'open'}
|
||||||
|
for root, dirs, files in os.walk(parent):
|
||||||
|
if root != old_root:
|
||||||
|
break
|
||||||
|
if dirs:
|
||||||
|
node[u'children'] = []
|
||||||
|
for dirpath in dirs:
|
||||||
|
full_dirpath = os.path.join(root, dirpath)
|
||||||
|
self._get_directories(full_dirpath, node[u'children'])
|
||||||
|
tree.append(node)
|
||||||
|
|
||||||
|
def _get_files(self, dirpath):
|
||||||
|
for root, dirs, files in os.walk(os.path.abspath(dirpath)):
|
||||||
|
filelist = []
|
||||||
|
for filename in files:
|
||||||
|
if os.path.splitext(filename)[1] in [u'.png', u'.jpg', u'.gif']:
|
||||||
|
filelist.append({u'name': filename, u'type': u'/images/file-image.png'})
|
||||||
|
else:
|
||||||
|
filelist.append({u'name': filename, u'type': u'/images/file-unknown.png'})
|
||||||
|
return filelist
|
||||||
|
break
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
self._add_javascript(u'jstree/jquery.jstree.js')
|
||||||
|
directories = []
|
||||||
|
path = os.path.join(config[u'paths.media'], u'user%s' % c.current_user.id)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
self._get_directories(path, directories)
|
||||||
|
c.directories = json.dumps(directories)
|
||||||
|
c.files = self._get_files(path)
|
||||||
|
c.page_title = u'Media Browser'
|
||||||
|
return render(u'/media/index.mako')
|
||||||
|
|
||||||
|
@jsonify
|
||||||
|
def get_files(self):
|
||||||
|
path = request.GET.get(u'path', u'').split(u',')
|
||||||
|
dirpath = os.path.join(config[u'paths.media'], *path)
|
||||||
|
log.debug("Path: %s", path)
|
||||||
|
return {u'results': self._get_files(dirpath)}
|
||||||
|
|
||||||
|
@jsonify
|
||||||
|
def create_directory(self):
|
||||||
|
"""
|
||||||
|
Create a directory, called via AJAX.
|
||||||
|
"""
|
||||||
|
dirname = request.GET.get(u'directory')
|
||||||
|
parent = request.GET.get(u'parent')
|
||||||
|
if dirname and parent:
|
||||||
|
parent = os.path.abspath(parent)
|
||||||
|
log.debug(parent)
|
||||||
|
#os.makedirs(os.path.join(parent, dirname))
|
||||||
|
return u'1'
|
||||||
|
else:
|
||||||
|
return u'0'
|
||||||
|
|
75
scribeengine/controllers/page.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.lib import utils
|
||||||
|
from scribeengine.model import Page
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class PageController(BaseController):
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
h.redirect_to('/')
|
||||||
|
|
||||||
|
def view(self, url):
|
||||||
|
c.page = Session.query(Page)\
|
||||||
|
.filter_by(url=url)\
|
||||||
|
.first()
|
||||||
|
#.filter_by(status=u'published')\
|
||||||
|
c.page_title = c.page.title
|
||||||
|
return render(u'/page/view.mako')
|
||||||
|
|
||||||
|
@authenticate(u'Add Pages')
|
||||||
|
def new(self):
|
||||||
|
c.page_title = u'New Page'
|
||||||
|
return render(u'/page/new.mako')
|
||||||
|
|
||||||
|
@authenticate(u'Edit My Pages')
|
||||||
|
def edit(self, id=None):
|
||||||
|
if id is None:
|
||||||
|
h.redirect_to(h.url_for(controller=u'page', action=u'new'))
|
||||||
|
c.page = Session.query(Page).get(id)
|
||||||
|
c.page_title = u'Edit Page: %s' % c.page.title
|
||||||
|
return render(u'/post/edit.mako')
|
||||||
|
|
||||||
|
@authenticate(u'Edit My Pages')
|
||||||
|
def edit_POST(self, id=None):
|
||||||
|
url = utils.generate_url(c.form_values[u'page-title'])
|
||||||
|
if id is None:
|
||||||
|
page = Page()
|
||||||
|
page.user = c.current_user
|
||||||
|
page.created = datetime.now()
|
||||||
|
else:
|
||||||
|
page = Session.query(Page).get(id)
|
||||||
|
page.modified = datetime.now()
|
||||||
|
page.title = c.form_values[u'page-title']
|
||||||
|
page.body = c.form_values[u'page-body']
|
||||||
|
page.url = url
|
||||||
|
Session.add(page)
|
||||||
|
Session.commit()
|
||||||
|
h.redirect_to('/' + str(page.url))
|
117
scribeengine/controllers/post.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from scribeengine.lib.base import *
|
||||||
|
from scribeengine.lib import utils
|
||||||
|
from scribeengine.model import Post, Tag
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class PostController(BaseController):
|
||||||
|
|
||||||
|
def __before__(self):
|
||||||
|
BaseController.__before__(self)
|
||||||
|
self._add_javascript(u'jquery.tag.editor.js')
|
||||||
|
self._add_javascript(u'jquery.tinymce.js')
|
||||||
|
self._add_javascript(u'ScribeEngine.Post.js')
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
h.redirect_to('/')
|
||||||
|
|
||||||
|
@authenticate(u'Add Posts')
|
||||||
|
def new(self):
|
||||||
|
c.page_title = u'New Post'
|
||||||
|
c.now = datetime.now()
|
||||||
|
return render(u'/post/new.mako')
|
||||||
|
|
||||||
|
@authenticate(u'Edit My Posts')
|
||||||
|
def edit(self, id=None):
|
||||||
|
if id is None:
|
||||||
|
h.redirect_to(h.url_for(controller=u'post', action=u'new'))
|
||||||
|
c.post = Session.query(Post).get(id)
|
||||||
|
if len(c.post.tags):
|
||||||
|
c.post.tags_list = u', '.join([tag.name for tag in c.post.tags])
|
||||||
|
else:
|
||||||
|
c.post.tags_list = u''
|
||||||
|
c.page_title = u'Edit Post: %s' % c.post.title
|
||||||
|
return render(u'/post/edit.mako')
|
||||||
|
|
||||||
|
@authenticate(u'Edit My Posts')
|
||||||
|
def edit_POST(self, id=None):
|
||||||
|
url = utils.generate_url(c.form_values[u'post-title'])
|
||||||
|
if id is None:
|
||||||
|
post = Post()
|
||||||
|
post.user = c.current_user
|
||||||
|
else:
|
||||||
|
post = Session.query(Post).get(id)
|
||||||
|
post.modified = datetime.now()
|
||||||
|
if c.form_values.get(u'post-authored') and c.form_values[u'post-authored']:
|
||||||
|
post.created = datetime(*time.strptime(c.form_values[u'post-authored'], '%Y/%m/%d %H:%M:%S')[0:6])
|
||||||
|
post.title = c.form_values[u'post-title']
|
||||||
|
post.body = c.form_values[u'post-body']
|
||||||
|
if c.form_values[u'post-action'] == u'Save Draft':
|
||||||
|
post.status = u'draft'
|
||||||
|
else:
|
||||||
|
post.status = u'published'
|
||||||
|
post.url = url
|
||||||
|
tags = c.form_values[u'post-tags']
|
||||||
|
tag_list = [tag_name.strip() for tag_name in tags.split(u',')]
|
||||||
|
tag_urls = [utils.generate_url(tag_name) for tag_name in tags.split(u',')]
|
||||||
|
db_tags = Session.query(Tag).filter(Tag.url.in_(tag_urls)).all()
|
||||||
|
post.tags = []
|
||||||
|
for tag in db_tags:
|
||||||
|
if tag.name in tag_list:
|
||||||
|
tag_list.remove(tag.name)
|
||||||
|
post.tags.append(tag)
|
||||||
|
for tag in tag_list:
|
||||||
|
post.tags.append(Tag(name=tag, url=utils.generate_url(tag)))
|
||||||
|
Session.add(post)
|
||||||
|
Session.commit()
|
||||||
|
if c.form_values[u'post-action'] == u'Save Draft':
|
||||||
|
h.redirect_to(h.url_for(controller=u'post', action=u'draft'))
|
||||||
|
else:
|
||||||
|
h.redirect_to(h.url_for_post(post))
|
||||||
|
|
||||||
|
def draft(self):
|
||||||
|
posts = Session.query(Post)\
|
||||||
|
.filter_by(status=u'draft')\
|
||||||
|
.order_by(Post.created.desc())
|
||||||
|
pagination = utils.paginate(posts, 10,
|
||||||
|
int(request.GET.get(u'page', 1)), '/')
|
||||||
|
c.posts = pagination[u'records']
|
||||||
|
if pagination[u'prev'] != pagination[u'page']:
|
||||||
|
c.first_page = pagination[u'first']
|
||||||
|
c.prev_page = pagination[u'prev']
|
||||||
|
if pagination[u'next'] != pagination[u'page']:
|
||||||
|
c.next_page = pagination[u'next']
|
||||||
|
c.last_page = pagination[u'last']
|
||||||
|
c.list_start = pagination[u'start']
|
||||||
|
c.list_total = pagination[u'total']
|
||||||
|
c.list_end = pagination[u'end']
|
||||||
|
c.page_title = u'Draft Posts'
|
||||||
|
return render(u'/post/draft.mako')
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2010-2021 Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This file is part of ScribeEngine. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is free software: you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
|
||||||
# any later version. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.db` module sets up SQLAlchemy
|
|
||||||
"""
|
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
|
||||||
|
|
||||||
# Get the db object
|
|
||||||
db = SQLAlchemy()
|
|
||||||
|
|
||||||
# Extract the objects to make them prettier
|
|
||||||
Model = db.Model
|
|
||||||
Table = db.Table
|
|
||||||
Column = db.Column
|
|
||||||
ForeignKey = db.ForeignKey
|
|
||||||
String = db.String
|
|
||||||
Text = db.Text
|
|
||||||
Integer = db.Integer
|
|
||||||
Boolean = db.Boolean
|
|
||||||
DateTime = db.DateTime
|
|
||||||
relationship = db.relationship
|
|
||||||
backref = db.backref
|
|
||||||
inspect = db.inspect
|
|
||||||
session = db.session
|
|
@ -1,123 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2010-2021 Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This file is part of ScribeEngine. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is free software: you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
|
||||||
# any later version. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.helpers` module contains some theme helper methods
|
|
||||||
"""
|
|
||||||
from flask import current_app
|
|
||||||
from flask_themes2 import get_theme, render_theme_template, template_exists as ft2_template_exists
|
|
||||||
from jinja2 import TemplateNotFound, contextfunction
|
|
||||||
|
|
||||||
from scribeengine.models import Menu, Site, Variable
|
|
||||||
|
|
||||||
|
|
||||||
@contextfunction
|
|
||||||
def template_exists(context, template_name):
|
|
||||||
"""
|
|
||||||
Add the template_exists() method into the template context
|
|
||||||
"""
|
|
||||||
return ft2_template_exists(template_name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_variable(name, default_value=None):
|
|
||||||
def _type_cast(type_, value, default_value):
|
|
||||||
try:
|
|
||||||
return type_(var.value)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
return default_value
|
|
||||||
|
|
||||||
var = Variable.get(name)
|
|
||||||
if var:
|
|
||||||
if var.type.lower().startswith('int'):
|
|
||||||
return _type_cast(int, var.value, default_value)
|
|
||||||
elif var.type.lower().startswith('float'):
|
|
||||||
return _type_cast(float, var.value, default_value)
|
|
||||||
elif var.type.lower().startswith('bool'):
|
|
||||||
return var.value.lower().strip() in ['yes', 'y', '1', 'true', 't']
|
|
||||||
return var.value
|
|
||||||
else:
|
|
||||||
return default_value
|
|
||||||
|
|
||||||
|
|
||||||
def get_site_details():
|
|
||||||
"""
|
|
||||||
Returns an object with the details of the site
|
|
||||||
"""
|
|
||||||
return Site(get_variable('site-name', 'Example Site'),
|
|
||||||
get_variable('site-slogan', 'From the Firehose'),
|
|
||||||
get_variable('site-about', ''))
|
|
||||||
|
|
||||||
|
|
||||||
def get_navigation(block='primary'):
|
|
||||||
"""
|
|
||||||
Get the navigation
|
|
||||||
"""
|
|
||||||
return Menu.query.filter(Menu.block == block).first()
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_theme():
|
|
||||||
"""
|
|
||||||
Determine the current theme.
|
|
||||||
"""
|
|
||||||
ident = current_app.config.get('THEME_DEFAULT', 'quill')
|
|
||||||
return get_theme(ident)
|
|
||||||
|
|
||||||
|
|
||||||
def render(template, **context):
|
|
||||||
"""
|
|
||||||
Render a template, after selecting a theme
|
|
||||||
"""
|
|
||||||
context.update({'site': get_site_details(), 'navigation': get_navigation()})
|
|
||||||
return render_theme_template(get_current_theme(), template, **context)
|
|
||||||
|
|
||||||
|
|
||||||
def render_node(node):
|
|
||||||
"""
|
|
||||||
Render a template, after selecting a theme
|
|
||||||
"""
|
|
||||||
context = {'node': node.complete}
|
|
||||||
template_names = ['/node-{}.html'.format(node.type), '/node.html']
|
|
||||||
for template in template_names:
|
|
||||||
if ft2_template_exists(template):
|
|
||||||
return render(template, **context)
|
|
||||||
raise TemplateNotFound('Could not find a node template')
|
|
||||||
|
|
||||||
|
|
||||||
def render_node_list(nodes):
|
|
||||||
"""
|
|
||||||
Render a list of nodes
|
|
||||||
"""
|
|
||||||
context = {'nodes': [node.complete for node in nodes]}
|
|
||||||
template_names = ['/node-list.html']
|
|
||||||
if nodes:
|
|
||||||
template_names.insert(0, '/node-{}-list.html'.format(nodes[0].type))
|
|
||||||
for template in template_names:
|
|
||||||
if ft2_template_exists(template):
|
|
||||||
return render(template, **context)
|
|
||||||
raise TemplateNotFound('Could not find a node-list template')
|
|
||||||
|
|
||||||
|
|
||||||
def render_admin(template, **context):
|
|
||||||
"""
|
|
||||||
Render a template, after selecting a theme
|
|
||||||
"""
|
|
||||||
context.update({'site': get_site_details()})
|
|
||||||
return render_theme_template('admin', template, **context)
|
|
0
scribeengine/lib/__init__.py
Normal file
@ -4,7 +4,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ScribeEngine - Open Source Blog Software #
|
# ScribeEngine - Open Source Blog Software #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# Copyright (c) 2010-2017 Raoul Snyman #
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
@ -19,17 +19,22 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.views.account` module contains the account views
|
|
||||||
"""
|
|
||||||
from flask import Blueprint
|
|
||||||
from pytz import all_timezones
|
|
||||||
|
|
||||||
from scribeengine.helpers import render
|
"""The application's Globals object"""
|
||||||
|
|
||||||
account = Blueprint('account', __name__, url_prefix='/account')
|
class Globals(object):
|
||||||
|
|
||||||
|
"""Globals acts as a container for objects available throughout the
|
||||||
|
life of the application
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""One instance of Globals is created during application
|
||||||
|
initialization and is available during requests via the
|
||||||
|
'app_globals' variable
|
||||||
|
|
||||||
|
"""
|
||||||
|
from turbomail.adapters import tm_pylons
|
||||||
|
tm_pylons.start_extension()
|
||||||
|
|
||||||
@account.route('', methods=['GET'])
|
|
||||||
def index():
|
|
||||||
return render('/account/index.html', page_title='My Account', timezones=all_timezones)
|
|
257
scribeengine/lib/base.py
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
The base Controller API
|
||||||
|
|
||||||
|
Provides the BaseController class for subclassing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from calendar import Calendar
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from decorator import decorator
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from paste.request import construct_url
|
||||||
|
from webob.exc import HTTPMovedPermanently
|
||||||
|
from pylons import c, request, session, response, config
|
||||||
|
from pylons.controllers import WSGIController
|
||||||
|
from pylons.templating import render_mako
|
||||||
|
from sqlalchemy.sql.expression import asc, desc
|
||||||
|
from formencode import Schema, Invalid
|
||||||
|
from monthdelta import monthdelta
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
from scribeengine.lib import helpers as h
|
||||||
|
from scribeengine.lib.validation import jsvalidate
|
||||||
|
from scribeengine.model.meta import Session
|
||||||
|
from scribeengine.model import Variable, User, Category, Page, Post
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class BaseController(WSGIController):
|
||||||
|
|
||||||
|
def __before__(self):
|
||||||
|
if session.get(u'REMOTE_USER'):
|
||||||
|
c.current_user = Session.query(User).get(session[u'REMOTE_USER'])
|
||||||
|
c.blog_title = Session.query(Variable).get(u'blog title').value
|
||||||
|
c.blog_slogan = Session.query(Variable).get(u'blog slogan').value
|
||||||
|
c.categories = Session.query(Category).order_by(Category.name.asc()).all()
|
||||||
|
c.pages = Session.query(Page).all()
|
||||||
|
c.calendar = Calendar(6)
|
||||||
|
server_tz = timezone(config.get(u'server.timezone', u'UTC'))
|
||||||
|
user_tz = c.current_user.timezone if c.current_user and c.current_user.timezone else u'UTC'
|
||||||
|
c.today = datetime.now(server_tz).astimezone(timezone(user_tz))
|
||||||
|
if not c.thismonth:
|
||||||
|
c.thismonth = c.today #datetime.utcnow().astimezone(timezone(c.current_user.timezone))
|
||||||
|
c.prev_month = c.thismonth - monthdelta(1)
|
||||||
|
c.next_month = c.thismonth + monthdelta(1)
|
||||||
|
month_start = datetime(c.thismonth.year, c.thismonth.month, 1, 0, 0, 0, 0)
|
||||||
|
month_end = c.next_month.replace(day=1, hour=23, minute=59, second=59, microsecond=9999) - timedelta(seconds=1)
|
||||||
|
posts = Session.query(Post)\
|
||||||
|
.filter(Post.created >= month_start)\
|
||||||
|
.filter(Post.created <= month_end)\
|
||||||
|
.all()
|
||||||
|
c.month_posts = {}
|
||||||
|
for post in posts:
|
||||||
|
if post.created.day not in c.month_posts:
|
||||||
|
c.month_posts[post.created.day] = []
|
||||||
|
c.month_posts[post.created.day].append(post)
|
||||||
|
self._add_javascript(u'jquery.js')
|
||||||
|
self._add_javascript(u'jquery.metadata.js')
|
||||||
|
if c.jsvalidation:
|
||||||
|
self._add_javascript(u'jquery.validate.js')
|
||||||
|
self._add_javascript(u'ScribeEngine.js')
|
||||||
|
self._add_jsinit(u'ScribeEngine.Init.js')
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
"""Invoke the Controller"""
|
||||||
|
# WSGIController.__call__ dispatches to the Controller method
|
||||||
|
# the request is routed to. This routing information is
|
||||||
|
# available in environ['pylons.routes_dict']
|
||||||
|
try:
|
||||||
|
# If there's a jsschema function, create a template context variable
|
||||||
|
# that will be used to call in the JavaScript validation.
|
||||||
|
route = u'/%s/%s' % (environ[u'pylons.routes_dict'][u'controller'],
|
||||||
|
environ[u'pylons.routes_dict'][u'action'])
|
||||||
|
action = environ[u'pylons.routes_dict'][u'action']
|
||||||
|
post = u'%s_POST' % action
|
||||||
|
jsschema = u'%s_jsschema' % action
|
||||||
|
schema = u'%s_schema' % action
|
||||||
|
if getattr(self, jsschema, None) is not None:
|
||||||
|
c.jsvalidation = route + u'_jsschema'
|
||||||
|
if environ[u'REQUEST_METHOD'].upper() == u'POST':
|
||||||
|
# Set up an initial, empty, set of form values.
|
||||||
|
c.form_values = {}
|
||||||
|
# Do validation according to schema here, even bfore it hits the <method>_POST
|
||||||
|
validators = getattr(self, schema, None)
|
||||||
|
if validators:
|
||||||
|
schema = self._make_schema(validators)
|
||||||
|
unvalidated_fields = {}
|
||||||
|
for key in schema.fields.keys():
|
||||||
|
if key in request.POST:
|
||||||
|
value = request.POST.getall(key)
|
||||||
|
if len(value) == 1:
|
||||||
|
unvalidated_fields[key] = value[0]
|
||||||
|
elif len(value) == 0:
|
||||||
|
unvalidated_fields[key] = None
|
||||||
|
else:
|
||||||
|
unvalidated_fields[key] = value
|
||||||
|
success, results = self._validate(schema, unvalidated_fields)
|
||||||
|
# Any error messages plus the form values are put into the
|
||||||
|
# c variable so that it is available to the called method.
|
||||||
|
c.form_errors = results[u'errors']
|
||||||
|
c.form_values = results[u'values']
|
||||||
|
if not success:
|
||||||
|
# If validation failed, go back to the original method.
|
||||||
|
return WSGIController.__call__(self, environ, start_response)
|
||||||
|
# Run through all of the POST variables, and make sure that
|
||||||
|
# we stick any remaining variables into c.form_values
|
||||||
|
for key in request.POST.keys():
|
||||||
|
if key not in c.form_values:
|
||||||
|
value = request.POST.getall(key)
|
||||||
|
if len(value) == 1:
|
||||||
|
c.form_values[key] = value[0]
|
||||||
|
elif len(value) == 0:
|
||||||
|
c.form_values[key] = None
|
||||||
|
else:
|
||||||
|
c.form_values[key] = value
|
||||||
|
# So, at this stage, we've done all is necessary, but this is
|
||||||
|
# a POST, so send us on to a POST method if it exists.
|
||||||
|
if getattr(self, post, None):
|
||||||
|
environ[u'pylons.routes_dict'][u'action'] = post
|
||||||
|
return WSGIController.__call__(self, environ, start_response)
|
||||||
|
finally:
|
||||||
|
Session.remove()
|
||||||
|
|
||||||
|
def _make_schema(self, validators_function):
|
||||||
|
"""
|
||||||
|
_make_schema is a method which is used to automatically convert a
|
||||||
|
dictionary of FormEncode validators into a schema.
|
||||||
|
"""
|
||||||
|
validators = validators_function()
|
||||||
|
return Schema(**validators)
|
||||||
|
|
||||||
|
def _validate(self, schema, params):
|
||||||
|
"""
|
||||||
|
Validate the (usually POST) request parameters against a FormEncode
|
||||||
|
schema.
|
||||||
|
|
||||||
|
Returns either:
|
||||||
|
|
||||||
|
`(True, values)`
|
||||||
|
Validation passed, and the values returned are converted (ie, of
|
||||||
|
the correct type and normalised - leading and/or trailing spaces
|
||||||
|
removed, etc.)
|
||||||
|
|
||||||
|
`(False, errors)`
|
||||||
|
Validation failed, and the errors returned are a dictionary:
|
||||||
|
|
||||||
|
`values`
|
||||||
|
The values as given in the request
|
||||||
|
|
||||||
|
`errors`
|
||||||
|
The validation errors (as many as can be given)
|
||||||
|
"""
|
||||||
|
if callable(schema):
|
||||||
|
validator = schema()
|
||||||
|
else:
|
||||||
|
validator = schema
|
||||||
|
# Remove 'submit', so that the schema doesn't need to contain it
|
||||||
|
if u'submit' in params:
|
||||||
|
del params[u'submit']
|
||||||
|
try:
|
||||||
|
values = validator.to_python(params)
|
||||||
|
except Invalid, e:
|
||||||
|
if e.error_dict:
|
||||||
|
# Standard case - each item in the schema that fails is added
|
||||||
|
# to error_dict with the key being the item name and the value
|
||||||
|
# a FormEncode error object.
|
||||||
|
error_dict = e.error_dict.copy()
|
||||||
|
else:
|
||||||
|
# On full-form validation failure (for example, sending additional
|
||||||
|
# data), the form isn't validated, and thus there is no error_dict
|
||||||
|
error_dict = {}
|
||||||
|
error_dict[None] = e.unpack_errors()
|
||||||
|
return False, {u'values': params, u'errors': error_dict}
|
||||||
|
return True, {u'values': values, u'errors': {}}
|
||||||
|
|
||||||
|
def _add_javascript(self, filename):
|
||||||
|
"""
|
||||||
|
This method dynamically adds javascript files to the <head> section
|
||||||
|
of the template.
|
||||||
|
"""
|
||||||
|
if not getattr(c, u'scripts', None):
|
||||||
|
c.scripts = []
|
||||||
|
c.scripts.append(filename)
|
||||||
|
|
||||||
|
def _add_jsinit(self, filename):
|
||||||
|
"""
|
||||||
|
This method dynamically includes a special javascript initialisation
|
||||||
|
file to the <head> section of the template.
|
||||||
|
"""
|
||||||
|
c.jsinit = filename
|
||||||
|
|
||||||
|
def _add_stylesheet(self, filename, subdir=None):
|
||||||
|
"""
|
||||||
|
This method dynamically adds javascript files to the <head> section
|
||||||
|
of the template.
|
||||||
|
"""
|
||||||
|
if not getattr(c, u'styles', None):
|
||||||
|
c.styles = []
|
||||||
|
if subdir is None:
|
||||||
|
subdir = c.theme_name
|
||||||
|
c.styles.append(filename, subdir)
|
||||||
|
|
||||||
|
|
||||||
|
def authenticate(permission=None):
|
||||||
|
"""
|
||||||
|
A decorator used to check the access level of a member against a permission.
|
||||||
|
"""
|
||||||
|
def validate(func, self, *args, **kwargs):
|
||||||
|
if session.get(u'REMOTE_USER'):
|
||||||
|
user = Session.query(User).get(session[u'REMOTE_USER'])
|
||||||
|
if user:
|
||||||
|
if permission and not user.has_permission(permission):
|
||||||
|
h.flash.set_message(
|
||||||
|
u'You don\'t have access to that area.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
h.flash.set_message(
|
||||||
|
u'You don\'t have access to that area.', u'error')
|
||||||
|
h.redirect_to('/')
|
||||||
|
else:
|
||||||
|
session[u'redirect_url'] = request.environ[u'PATH_INFO']
|
||||||
|
session.save()
|
||||||
|
h.flash.set_message(u'You need to be logged in to do that.', u'error')
|
||||||
|
h.redirect_to('/account/login')
|
||||||
|
return decorator(validate)
|
||||||
|
|
||||||
|
|
||||||
|
def render(template):
|
||||||
|
if request.environ['PATH_INFO'] == '/':
|
||||||
|
c.page_title = u'%s | %s ' % (c.blog_title, c.blog_slogan)
|
||||||
|
else:
|
||||||
|
c.page_title = u'%s | %s ' % (c.page_title, c.blog_title)
|
||||||
|
return render_mako(template)
|
||||||
|
|
101
scribeengine/lib/helpers.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Helper functions
|
||||||
|
|
||||||
|
Consists of functions to typically be used within templates, but also
|
||||||
|
available to Controllers. This module is available to both as 'h'.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from webhelpers.html import escape, HTML, literal, url_escape
|
||||||
|
from webhelpers.date import distance_of_time_in_words
|
||||||
|
from pylons import session, url as url_for
|
||||||
|
from pylons.controllers.util import redirect_to
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Flash(object):
|
||||||
|
def set_message(self, message_text, message_type, message_head=None):
|
||||||
|
session[u'flash.text'] = message_text
|
||||||
|
session[u'flash.type'] = message_type
|
||||||
|
session[u'flash.head'] = message_head
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
def has_message(self):
|
||||||
|
return session.get(u'flash.text')
|
||||||
|
|
||||||
|
def has_header(self):
|
||||||
|
return session.get(u'flash.head')
|
||||||
|
|
||||||
|
def get_message_text(self):
|
||||||
|
message_text = session.pop(u'flash.text', None)
|
||||||
|
if not message_text:
|
||||||
|
return None
|
||||||
|
session.save()
|
||||||
|
return message_text
|
||||||
|
|
||||||
|
def get_message_type(self):
|
||||||
|
message_type = session.pop(u'flash.type', None)
|
||||||
|
if not message_type:
|
||||||
|
return None
|
||||||
|
session.save()
|
||||||
|
return message_type
|
||||||
|
|
||||||
|
|
||||||
|
def teaser(text):
|
||||||
|
position = text.find(u'</p>')
|
||||||
|
if position > 0:
|
||||||
|
return text[:position + 4]
|
||||||
|
elif len(text) > 300:
|
||||||
|
text = text[:297]
|
||||||
|
position = len(text) - 1
|
||||||
|
while position > 0 and text[position] not in [u'<', u'>']:
|
||||||
|
position -= 1
|
||||||
|
if position != 0 and text[position + 1] == u'/':
|
||||||
|
position -= 1
|
||||||
|
while position > 0 and text[position] not in [u'<']:
|
||||||
|
position -= 1
|
||||||
|
if position != 0 and text[position] == u'<':
|
||||||
|
text = text[:position]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def comment(text):
|
||||||
|
text = text.replace(u'\r\n', u'\n')
|
||||||
|
paralist = escape(text).split(u'\n\n')
|
||||||
|
paragraphs = u'</p><p>'.join([para.strip(u'\n') for para in paralist])
|
||||||
|
text = paragraphs.replace(u'\n', u'<br />')
|
||||||
|
return literal(text)
|
||||||
|
|
||||||
|
def url_for_post(post):
|
||||||
|
#TODO: this is hard coded.
|
||||||
|
return url_for(
|
||||||
|
controller=u'blog',
|
||||||
|
action=u'view',
|
||||||
|
year=post.created.strftime('%Y'),
|
||||||
|
month=post.created.strftime('%m'),
|
||||||
|
day=post.created.strftime('%d'),
|
||||||
|
url=post.url
|
||||||
|
)
|
||||||
|
|
||||||
|
flash = Flash()
|
211
scribeengine/lib/utils.py
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import re
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import string
|
||||||
|
from random import choice
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pylons import config, c
|
||||||
|
from turbomail import Message
|
||||||
|
|
||||||
|
from scribeengine.lib.base import render, h
|
||||||
|
|
||||||
|
class KeywordProcessor(object):
|
||||||
|
"""Process user-supplied keywords, tags, or search terms.
|
||||||
|
|
||||||
|
This tries to be as flexible as possible while being efficient.
|
||||||
|
The vast majority of the work is done in the regular expression."""
|
||||||
|
|
||||||
|
def __init__(self, separators=' \t', quotes=['"', "'"], groups=[], group=False, normalize=None, sort=False, result=list):
|
||||||
|
"""Configure the processor.
|
||||||
|
|
||||||
|
separators: A list of acceptable separator characters. The first will be used for joins.
|
||||||
|
quotes: Pass a list or tuple of allowable quotes. E.g. ["\"", "'"] or None to disable.
|
||||||
|
groups: Pass a string, list, or tuple of allowable prefixes. E.g. '+-' or None to disable.
|
||||||
|
group: Pass in the type you want to group by, e.g. list, tuple, or dict.
|
||||||
|
normalize: Pass a function which will normalize the results. E.g. lambda s: s.lower().strip(' \"')
|
||||||
|
sort: Sort the resulting list (or lists) alphabeticlly.
|
||||||
|
result: The return type. One of set, tuple, list.
|
||||||
|
|
||||||
|
If groups are defined, and group is not, the result will be a list/tuple/set of tuples, e.g. [('+', "foo"), ...]
|
||||||
|
"""
|
||||||
|
separators = list(separators)
|
||||||
|
self.pattern = ''.join((
|
||||||
|
('[\s%s]*' % (''.join(separators), )), # Trap possible leading space or separators.
|
||||||
|
'(',
|
||||||
|
('[%s]%s' % (''.join([i for i in list(groups) if i is not None]), '?' if None in groups else '')) if groups else '', # Pass groups=('+','-') to handle optional leading + or -.
|
||||||
|
''.join([(r'%s[^%s]+%s|' % (i, i, i)) for i in quotes]) if quotes else '', # Match any amount of text (that isn't a quote) inside quotes.
|
||||||
|
('[^%s]+' % (''.join(separators), )), # Match any amount of text that isn't whitespace.
|
||||||
|
')',
|
||||||
|
('[%s]*' % (''.join(separators), )), # Match possible separator character.
|
||||||
|
))
|
||||||
|
self.regex = re.compile(self.pattern)
|
||||||
|
self.groups = list(groups)
|
||||||
|
self.group = dict if group is True else group
|
||||||
|
self.normalize = normalize
|
||||||
|
self.sort = sort
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
def split(self, value):
|
||||||
|
if not isinstance(value, basestring):
|
||||||
|
raise TypeError("Invalid type for argument 'value'.")
|
||||||
|
matches = self.regex.findall(value)
|
||||||
|
if callable(self.normalize):
|
||||||
|
matches = [self.normalize(i) for i in matches]
|
||||||
|
if self.sort:
|
||||||
|
matches.sort()
|
||||||
|
if not self.groups:
|
||||||
|
return self.result(matches)
|
||||||
|
groups = dict([(i, list()) for i in self.groups])
|
||||||
|
if None not in groups.iterkeys():
|
||||||
|
groups[None] = list() # To prevent errors.
|
||||||
|
for i in matches:
|
||||||
|
if i[0] in self.groups:
|
||||||
|
groups[i[0]].append(i[1:])
|
||||||
|
else:
|
||||||
|
groups[None].append(i)
|
||||||
|
if self.group is dict:
|
||||||
|
return groups
|
||||||
|
if self.group is False or self.group is None:
|
||||||
|
results = []
|
||||||
|
for group in self.groups:
|
||||||
|
results.extend([(group, match) for match in groups[group]])
|
||||||
|
return self.result(results)
|
||||||
|
return self.group([[match for match in groups[group]] for group in self.groups])
|
||||||
|
|
||||||
|
|
||||||
|
def send_mail(template, mail_to, mail_from, subject, variables={}, attachments=[]):
|
||||||
|
"""
|
||||||
|
Sends an e-mail using the template ``template``.
|
||||||
|
|
||||||
|
``template``
|
||||||
|
The template to use.
|
||||||
|
|
||||||
|
``mail_to``
|
||||||
|
One or more addresses to send the e-mail to.
|
||||||
|
|
||||||
|
``mail_from``
|
||||||
|
The address to send e-mail from.
|
||||||
|
|
||||||
|
``subject``
|
||||||
|
The subject of the e-mail.
|
||||||
|
|
||||||
|
``variables``
|
||||||
|
Variables to be used in the template.
|
||||||
|
|
||||||
|
``attachments``
|
||||||
|
If you want to attach files to the e-mail, use this list.
|
||||||
|
"""
|
||||||
|
for name, value in variables.iteritems():
|
||||||
|
setattr(c, name, value)
|
||||||
|
message = Message(mail_from, mail_to, subject)
|
||||||
|
message.plain = render(template)
|
||||||
|
message.send()
|
||||||
|
|
||||||
|
def generate_url(title):
|
||||||
|
"""
|
||||||
|
Generate a friendly URL from a blog post title.
|
||||||
|
|
||||||
|
``title``
|
||||||
|
The title of the blog post.
|
||||||
|
"""
|
||||||
|
return re.sub(r'[^a-zA-Z0-9]+', u'-', title.lower()).strip('-')
|
||||||
|
|
||||||
|
def hash_password(password):
|
||||||
|
"""
|
||||||
|
Return an HMAC SHA256 hash of a password.
|
||||||
|
|
||||||
|
``password``
|
||||||
|
The password to hash.
|
||||||
|
"""
|
||||||
|
return unicode(hmac.new(config[u'security.salt'], password,
|
||||||
|
hashlib.sha256).hexdigest(), 'utf-8')
|
||||||
|
|
||||||
|
def generate_key(length):
|
||||||
|
"""
|
||||||
|
Generate a random set of letters and numbers of length ``length``. Usually
|
||||||
|
used to generate activation keys.
|
||||||
|
|
||||||
|
``length``
|
||||||
|
The length of the key.
|
||||||
|
"""
|
||||||
|
return ''.join([choice(string.letters + string.digits) for i in range(length)])
|
||||||
|
|
||||||
|
def month_first_day(datetime):
|
||||||
|
"""
|
||||||
|
Returns a modified datetime with the day being midnight of the first day of
|
||||||
|
the month, given a datetime object.
|
||||||
|
"""
|
||||||
|
return datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
def month_last_day(datetime):
|
||||||
|
"""
|
||||||
|
Returns a modified datetime with the day being the last day of the month,
|
||||||
|
given a datetime object.
|
||||||
|
"""
|
||||||
|
if datetime.month in [1, 3, 5, 7, 8, 10, 12]:
|
||||||
|
day = 31
|
||||||
|
elif datetime.month in [4, 6, 9, 11]:
|
||||||
|
day = 30
|
||||||
|
else:
|
||||||
|
if datetime.year % 4 == 0 and datetime.year % 100 != 0 or datetime.year % 400 == 0:
|
||||||
|
day = 29
|
||||||
|
else:
|
||||||
|
day = 28
|
||||||
|
return datetime.replace(day=day, hour=23, minute=59, second=59, microsecond=99999)
|
||||||
|
|
||||||
|
def paginate(query, page_size, current_page, base_url=None):
|
||||||
|
query_count = query.count()
|
||||||
|
page_count = query_count / page_size
|
||||||
|
if query_count % page_size > 0:
|
||||||
|
page_count += 1
|
||||||
|
offset = (current_page - 1) * page_size
|
||||||
|
records = query.offset(offset).limit(page_size).all()
|
||||||
|
prev_page = current_page - 1 if current_page - 1 >= 1 else 1
|
||||||
|
next_page = current_page + 1 if current_page + 1 <= page_count else page_count
|
||||||
|
if base_url:
|
||||||
|
return {
|
||||||
|
u'page': h.url_for(base_url, page=current_page),
|
||||||
|
u'first': h.url_for(base_url, page=1),
|
||||||
|
u'prev': h.url_for(base_url, page=prev_page),
|
||||||
|
u'next': h.url_for(base_url, page=next_page),
|
||||||
|
u'last': h.url_for(base_url, page=page_count),
|
||||||
|
u'start': offset + 1,
|
||||||
|
u'end': offset + len(records),
|
||||||
|
u'total': query_count,
|
||||||
|
u'records': records
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
u'page': current_page,
|
||||||
|
u'first': 1,
|
||||||
|
u'prev': prev_page,
|
||||||
|
u'next': next_page,
|
||||||
|
u'last': page_count,
|
||||||
|
u'start': offset + 1,
|
||||||
|
u'end': offset + len(records),
|
||||||
|
u'total': query_count,
|
||||||
|
u'records': records
|
||||||
|
}
|
177
scribeengine/lib/validation/__init__.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Validation helper classes and methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from formencode.htmlfill import FillingParser
|
||||||
|
from decorator import decorator
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ClassFillingParser(FillingParser):
|
||||||
|
"""
|
||||||
|
This parser inherits from the base htmlfill.FillingParser, but overrides
|
||||||
|
the behaviour.
|
||||||
|
|
||||||
|
If it encounters the class `error_${name}` on an element and the name given
|
||||||
|
is in the error dictionary, then the `form-error` class is added to the
|
||||||
|
element. Otherwise, the class is removed.
|
||||||
|
|
||||||
|
If it encounters the class `errormessage_${name}` on an element and the name
|
||||||
|
given is in the error dictionary, then the content of the element is
|
||||||
|
replaced with the error from the error dictionary. Otherwise, the element
|
||||||
|
is removed.
|
||||||
|
|
||||||
|
The class `error_` (ie, no name) will give any form errors.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Set up the filling parser.
|
||||||
|
"""
|
||||||
|
self.formname = kwargs.pop(u'formname', None)
|
||||||
|
self.in_errormessage = None
|
||||||
|
return FillingParser.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs, startend=False):
|
||||||
|
"""
|
||||||
|
Handle the start of an HTML tag.
|
||||||
|
"""
|
||||||
|
# Find any class attribute on the element
|
||||||
|
class_attr = None
|
||||||
|
for key, value in attrs:
|
||||||
|
if key == u'class':
|
||||||
|
class_attr = value
|
||||||
|
break
|
||||||
|
# Only have to handle the case where there is a class.
|
||||||
|
if class_attr:
|
||||||
|
classes = class_attr.split(u' ')
|
||||||
|
for class_name in classes:
|
||||||
|
# Replace error_${name} with "error" if name is in error dict
|
||||||
|
if class_name.startswith(u'error_'):
|
||||||
|
classes = [cls for cls in classes if cls != class_name]
|
||||||
|
field = class_name[6:]
|
||||||
|
if field in self.errors:
|
||||||
|
classes.append(u'form-error')
|
||||||
|
self.set_attr(attrs, u'class', u' '.join(classes))
|
||||||
|
self.write_tag(tag, attrs, startend)
|
||||||
|
self.skip_next = True
|
||||||
|
return
|
||||||
|
# Replace the contents of elements with class
|
||||||
|
# errormessage_${name} with the error from the error
|
||||||
|
# dictionary (or delete the element entirely if there is no
|
||||||
|
# such error).
|
||||||
|
if class_name.startswith(u'errormessage_'):
|
||||||
|
field = class_name[13:]
|
||||||
|
self.in_errormessage = tag
|
||||||
|
self.skip_error = True
|
||||||
|
# form errors
|
||||||
|
if not field:
|
||||||
|
field = None
|
||||||
|
if field in self.errors:
|
||||||
|
classes = [cls for cls in classes if cls != class_name]
|
||||||
|
classes.append(u'form-error')
|
||||||
|
self.set_attr(attrs, u'class', u' '.join(classes))
|
||||||
|
self.write_tag(tag, attrs, startend)
|
||||||
|
self.write_text(htmlliteral(unicode(self.errors[field])).text)
|
||||||
|
self.write_text(u'</%s>' % tag)
|
||||||
|
self.skip_next = True
|
||||||
|
return
|
||||||
|
return FillingParser.handle_starttag(self, tag, attrs, startend=False)
|
||||||
|
|
||||||
|
def handle_endtag(self, tag):
|
||||||
|
"""
|
||||||
|
Handle the ending HTML tag.
|
||||||
|
"""
|
||||||
|
FillingParser.handle_endtag(self, tag)
|
||||||
|
# We're handling skipping of contents of an element with
|
||||||
|
# errormessage_* on it.
|
||||||
|
#
|
||||||
|
# After we encounter the end tag, we can stop ignoring elements.
|
||||||
|
if self.in_errormessage == tag:
|
||||||
|
self.skip_error = False
|
||||||
|
self.in_errormessage = None
|
||||||
|
self.skip_next = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def html_error_fill(cls, form, errors_and_values):
|
||||||
|
"""
|
||||||
|
Create the custom ClassFillingParser, and pass through the values,
|
||||||
|
errors, and formname from the errors dictionary.
|
||||||
|
|
||||||
|
Converts the incoming form from UTF-8-encoded byte strings to Unicode
|
||||||
|
strings.
|
||||||
|
"""
|
||||||
|
p = cls(defaults=errors_and_values[u'form_values'],
|
||||||
|
errors=errors_and_values[u'form_errors'],
|
||||||
|
auto_error_formatter=False)
|
||||||
|
p.feed(form.decode(u'utf-8'))
|
||||||
|
p.close()
|
||||||
|
return p.text().encode(u'utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def jsvalidate(form_id):
|
||||||
|
"""
|
||||||
|
This decorator is used to generate JavaScript for client-side validate.
|
||||||
|
|
||||||
|
``form_id``
|
||||||
|
The HTML ``id`` of the form to be validated.
|
||||||
|
"""
|
||||||
|
def entangle(func, self, *args, **kwargs):
|
||||||
|
default_jscript = u"""/* jQuery Validation */
|
||||||
|
$ProjectHQ.Events.bind_load(function () {
|
||||||
|
$("#%s").validate({
|
||||||
|
errorClass: "form-error",
|
||||||
|
errorContainer: "#form-errors",
|
||||||
|
errorLabelContainer: "#form-errors > ul",
|
||||||
|
wrapper: "li",
|
||||||
|
rules: {
|
||||||
|
%s
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
%s
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});"""
|
||||||
|
validators = []
|
||||||
|
messages = []
|
||||||
|
jsschema = func(self, *args, **kwargs)
|
||||||
|
for name, validator in jsschema.iteritems():
|
||||||
|
validators.append(u'%s: {%s}' % (name, validator.to_javascript()))
|
||||||
|
if validator.get_message() is not None:
|
||||||
|
validator_message = validator.get_message()
|
||||||
|
if isinstance(validator_message, basestring):
|
||||||
|
messages.append(u'%s: "%s"' % (name, validator.get_message()))
|
||||||
|
else:
|
||||||
|
validator_messages = []
|
||||||
|
for key, value in validator_message.iteritems():
|
||||||
|
validator_messages.append(u'%s: "%s"' % (key, value))
|
||||||
|
messages.append(u'%s: {\n %s\n }' %
|
||||||
|
(name, ',\n '.join(validator_messages)))
|
||||||
|
jscript = default_jscript % (form_id, u',\n '.join(validators),
|
||||||
|
u',\n '.join(messages))
|
||||||
|
return jscript
|
||||||
|
return decorator(entangle)
|
||||||
|
|
230
scribeengine/lib/validation/client.py
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Client-side validators.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class JSValidator(object):
|
||||||
|
"""
|
||||||
|
This is a class used to create a validation rule in javascript.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
The constructor is used to create the validator.
|
||||||
|
|
||||||
|
field
|
||||||
|
The name of the field.
|
||||||
|
required
|
||||||
|
Whether or not this field is required.
|
||||||
|
type
|
||||||
|
The type of field. Can be "string", "number", "email", "integer"
|
||||||
|
"""
|
||||||
|
if u'type' not in kwargs:
|
||||||
|
raise KeyError(u'"type" is a required argument.')
|
||||||
|
self.validators = {}
|
||||||
|
for key, arg in kwargs.iteritems():
|
||||||
|
self.validators[key] = arg
|
||||||
|
|
||||||
|
def to_javascript(self):
|
||||||
|
js_validators = []
|
||||||
|
for key, value in self.validators.iteritems():
|
||||||
|
if key == u'message':
|
||||||
|
continue
|
||||||
|
elif key == u'type':
|
||||||
|
if value in ['email', 'number', 'url']:
|
||||||
|
key = value
|
||||||
|
value = u'true'
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
#elif key == u'checked':
|
||||||
|
# key = 'required'
|
||||||
|
# if value:
|
||||||
|
# value = u'checked'
|
||||||
|
# else:
|
||||||
|
# value = u'unchecked'
|
||||||
|
elif key == u'condition':
|
||||||
|
conditions = value.split(u',')
|
||||||
|
values = []
|
||||||
|
for condition in conditions:
|
||||||
|
subconditions = condition.split(u';')
|
||||||
|
subvalues = []
|
||||||
|
for subcondition in subconditions:
|
||||||
|
if subcondition.find(u'==') >= 0:
|
||||||
|
parts = subcondition.split(u'==')
|
||||||
|
subvalues.append(u'$("%s").val() == %s' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u'>=') >= 0:
|
||||||
|
parts = subcondition.split(u'>=')
|
||||||
|
subvalues.append(u'$("%s").val() >= %s' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u'<=') >= 0:
|
||||||
|
parts = subcondition.split(u'<=')
|
||||||
|
subvalues.append(u'$("%s").val() <= %s' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u'>') >= 0:
|
||||||
|
parts = subcondition.split(u'>')
|
||||||
|
subvalues.append(u'$("%s").val() > %s' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u'<') >= 0:
|
||||||
|
parts = subcondition.split(u'<')
|
||||||
|
subvalues.append(u'$("%s").val() < %s' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u'!=') >= 0:
|
||||||
|
parts = subcondition.split(u'!=')
|
||||||
|
subvalues.append(u'$("%s").val() != %s' % (parts[0], parts[1]))
|
||||||
|
#elif subcondition.find(u'@') >= 0:
|
||||||
|
# parts = subcondition.split(u':')
|
||||||
|
# subvalues.append(u'$("%s").attr(%s)' % (parts[0], parts[1]))
|
||||||
|
elif subcondition.find(u':') >= 0:
|
||||||
|
parts = subcondition.split(u':')
|
||||||
|
subvalues.append(u'$("%s").is(":%s")' % (parts[0], parts[1]))
|
||||||
|
else:
|
||||||
|
subvalues.append(u'$("%s")' % subcondition)
|
||||||
|
values.append(u' && '.join(subvalues))
|
||||||
|
value = u'function () { return (%s); }' % u') || ('.join(values)
|
||||||
|
key = u'required'
|
||||||
|
elif isinstance(value, bool):
|
||||||
|
if value:
|
||||||
|
value = u'true'
|
||||||
|
else:
|
||||||
|
value = u'false'
|
||||||
|
elif isinstance(value, basestring):
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = unicode(value, u'utf-8')
|
||||||
|
value = u'"%s"' % value
|
||||||
|
else:
|
||||||
|
value = unicode(value)
|
||||||
|
js_validators.append(u'%s: %s' % (key, value))
|
||||||
|
return u', '.join(js_validators)
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
"""
|
||||||
|
If a message is set for this validator, return it, else return None.
|
||||||
|
"""
|
||||||
|
if 'message' in self.validators:
|
||||||
|
return self.validators['message']
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class JSNumber(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for numbers.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'number'
|
||||||
|
if 'condition' not in kwargs:
|
||||||
|
kwargs['required'] = True
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSDigits(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for digits.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'digits'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSString(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for strings.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'string'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSName(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for names.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'string'
|
||||||
|
kwargs['realname'] = True
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSAlphanumeric(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for alphanumeric strings.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'string'
|
||||||
|
kwargs['alphanumeric'] = True
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSEmail(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for strings.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'email'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSUrl(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for strings.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'url'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSDate(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a specialised version of JSValidator for dates.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['type'] = u'date'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSDropdown(JSValidator):
|
||||||
|
"""
|
||||||
|
This is a copy of the DropdownValidator for Formencode.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if 'invalid_option' not in kwargs:
|
||||||
|
invalid_option = 0
|
||||||
|
else:
|
||||||
|
invalid_option = kwargs['invalid_option']
|
||||||
|
del kwargs['invalid_option']
|
||||||
|
if 'condition' not in kwargs:
|
||||||
|
kwargs['required'] = True
|
||||||
|
kwargs['notvalue'] = invalid_option
|
||||||
|
kwargs['type'] = u'string'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JSCheckbox(JSValidator):
|
||||||
|
"""
|
||||||
|
A validator for maching sure checkboxes are checked.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if 'checked' not in kwargs:
|
||||||
|
kwargs['checked'] = True
|
||||||
|
kwargs['type'] = u'string'
|
||||||
|
JSValidator.__init__(self, *args, **kwargs)
|
@ -4,7 +4,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ScribeEngine - Open Source Blog Software #
|
# ScribeEngine - Open Source Blog Software #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# Copyright (c) 2010-2017 Raoul Snyman #
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
@ -19,24 +19,40 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The :mod:`~scribeengine.admin` module sets up the admin interface
|
Server-side validators.
|
||||||
"""
|
"""
|
||||||
from flask import Blueprint
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
# from scribeengine.db import session
|
from formencode.api import FancyValidator, Invalid
|
||||||
from scribeengine.helpers import render_admin
|
from formencode.validators import UnicodeString, Int, Email, FieldsMatch
|
||||||
from scribeengine.models import Post
|
|
||||||
|
|
||||||
admin = Blueprint('admin', __name__, url_prefix='/admin')
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Password(FancyValidator):
|
||||||
|
"""
|
||||||
|
This validator checks for a decently secure password. The password has to
|
||||||
|
contain a minimum of 6 characters, at least 1 number.
|
||||||
|
"""
|
||||||
|
regex = re.compile(r'^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[-.!@#%&]).{6,}$')
|
||||||
|
|
||||||
@admin.route('', methods=['GET'])
|
messages = {
|
||||||
def index():
|
u'insecure': u'Your password must be longer than 6 characters and '
|
||||||
return render_admin('index.html')
|
u'must have at least 1 capital letter, 1 number and one '
|
||||||
|
u'of the following characters: - . ~ @ # %% &'
|
||||||
|
}
|
||||||
|
|
||||||
|
def _to_python(self, value, state):
|
||||||
|
# _to_python gets run before validate_python. Here we
|
||||||
|
# strip whitespace off the password, because leading and
|
||||||
|
# trailing whitespace in a password is too elite.
|
||||||
|
return value.strip()
|
||||||
|
|
||||||
|
def validate_python(self, value, state):
|
||||||
|
if len(value) < self.min:
|
||||||
|
raise Invalid(self.message(u'insecure', state), value, state)
|
||||||
|
if not self.regex.match(value):
|
||||||
|
raise Invalid(self.message(u'insecure', state), value, state)
|
||||||
|
|
||||||
@admin.route('/posts', methods=['GET'])
|
|
||||||
def posts():
|
|
||||||
posts = Post.query.limit(10).all()
|
|
||||||
return render_admin('posts.html', posts=posts)
|
|
79
scribeengine/model/__init__.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
The application's model objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy.orm import mapper, relation, backref
|
||||||
|
|
||||||
|
from scribeengine.model import meta
|
||||||
|
from scribeengine.model.tables import categories_table, comments_table, \
|
||||||
|
files_table, media_types_table, pages_table, permissions_table, \
|
||||||
|
posts_table, roles_table, tags_table, users_table, variables_table, \
|
||||||
|
categories_posts_table, permissions_roles_table, posts_tags_table, \
|
||||||
|
roles_users_table
|
||||||
|
from scribeengine.model.classes import Category, Comment, File, MediaType, \
|
||||||
|
Page, Permission, Post, Role, Tag, User, Variable
|
||||||
|
|
||||||
|
def init_model(engine):
|
||||||
|
"""Call me before using any of the tables or classes in the model"""
|
||||||
|
meta.Session.configure(bind=engine)
|
||||||
|
meta.engine = engine
|
||||||
|
|
||||||
|
mapper(Category, categories_table)
|
||||||
|
mapper(Comment, comments_table)
|
||||||
|
mapper(File, files_table)
|
||||||
|
mapper(MediaType, media_types_table,
|
||||||
|
properties={
|
||||||
|
u'files': relation(File, backref=u'media_type')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mapper(Page, pages_table)
|
||||||
|
mapper(Permission, permissions_table)
|
||||||
|
mapper(Post, posts_table,
|
||||||
|
properties={
|
||||||
|
u'categories': relation(Category, backref=u'posts',
|
||||||
|
secondary=categories_posts_table),
|
||||||
|
u'comments': relation(Comment, backref=u'post',
|
||||||
|
order_by=Comment.created.asc()),
|
||||||
|
u'tags': relation(Tag,
|
||||||
|
backref=backref(u'posts', order_by='posts.created DESC'),
|
||||||
|
secondary=posts_tags_table)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mapper(Role, roles_table,
|
||||||
|
properties={
|
||||||
|
u'permissions': relation(Permission, backref=u'roles',
|
||||||
|
secondary=permissions_roles_table)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mapper(Tag, tags_table)
|
||||||
|
mapper(User, users_table,
|
||||||
|
properties={
|
||||||
|
u'comments': relation(Comment, backref=u'user'),
|
||||||
|
u'files': relation(File, backref=u'user'),
|
||||||
|
u'posts': relation(Post, backref=u'user'),
|
||||||
|
u'roles': relation(Role, backref=u'users', secondary=roles_users_table)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mapper(Variable, variables_table)
|
139
scribeengine/model/classes.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module contains the Class definitions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class BaseModel(object):
|
||||||
|
"""
|
||||||
|
A base model class which all the other classes inherit from. This provides
|
||||||
|
all model classes with a set of utility methods.
|
||||||
|
"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""
|
||||||
|
This constructor will set all the classes properties based on the
|
||||||
|
keyword arguments supplied.
|
||||||
|
"""
|
||||||
|
for keyword, argument in kwargs.iteritems():
|
||||||
|
setattr(self, keyword, argument)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if hasattr(self, 'id'):
|
||||||
|
return '<%s id=%s>' % (self.__class__.__name__, self.id)
|
||||||
|
|
||||||
|
class Category(BaseModel):
|
||||||
|
"""
|
||||||
|
This is a category for blog posts.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(BaseModel):
|
||||||
|
"""
|
||||||
|
All blog posts have comments. This is a single comment.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class File(BaseModel):
|
||||||
|
"""
|
||||||
|
A file in the media library.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MediaType(BaseModel):
|
||||||
|
"""
|
||||||
|
Distinguishes between different types of media.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Page(BaseModel):
|
||||||
|
"""
|
||||||
|
A page on the blog. This is separate from a blog entry, for things like
|
||||||
|
about pages.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Permission(BaseModel):
|
||||||
|
"""
|
||||||
|
A single permission.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Post(BaseModel):
|
||||||
|
"""
|
||||||
|
The most import part of all of this, the blog post.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Role(BaseModel):
|
||||||
|
"""
|
||||||
|
A role defines a set of permissions.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(BaseModel):
|
||||||
|
"""
|
||||||
|
A tag, an unstructured category, for blog posts.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
"""
|
||||||
|
The user.
|
||||||
|
"""
|
||||||
|
def has_permission(self, permission):
|
||||||
|
if isinstance(permission, basestring):
|
||||||
|
for role in self.roles:
|
||||||
|
for perm in role.permissions:
|
||||||
|
if perm.name == permission:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
elif isinstance(permission, Permission):
|
||||||
|
for role in self.roles:
|
||||||
|
for perm in role.permissions:
|
||||||
|
if perm == permission:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
elif isinstance(permission, list):
|
||||||
|
for role in self.roles:
|
||||||
|
for perm in role.permissions:
|
||||||
|
if perm.name in permission:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Variable(BaseModel):
|
||||||
|
"""
|
||||||
|
System variables.
|
||||||
|
"""
|
||||||
|
pass
|
@ -4,7 +4,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ScribeEngine - Open Source Blog Software #
|
# ScribeEngine - Open Source Blog Software #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# Copyright (c) 2010-2017 Raoul Snyman #
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
@ -19,6 +19,22 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The :mod:`~scribeengine.views` module contains the views for ScribeEngine
|
SQLAlchemy Metadata and Session object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import MetaData
|
||||||
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
|
||||||
|
__all__ = ['Session', 'engine', 'metadata']
|
||||||
|
|
||||||
|
# SQLAlchemy database engine. Updated by model.init_model()
|
||||||
|
engine = None
|
||||||
|
|
||||||
|
# SQLAlchemy session manager. Updated by model.init_model()
|
||||||
|
Session = scoped_session(sessionmaker())
|
||||||
|
|
||||||
|
# Global metadata. If you have multiple databases with overlapping table
|
||||||
|
# names, you'll need a metadata for each database
|
||||||
|
metadata = MetaData()
|
154
scribeengine/model/tables.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# ScribeEngine - Open Source Blog Software #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2010 Raoul Snyman #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module contains the table definitions.
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy import Table, Column, ForeignKey
|
||||||
|
from sqlalchemy.types import Unicode, Integer, UnicodeText, DateTime
|
||||||
|
|
||||||
|
from scribeengine.model.meta import metadata
|
||||||
|
|
||||||
|
# Definition of the "categories" table
|
||||||
|
categories_table = Table(u'categories', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'name', Unicode(100), nullable=False),
|
||||||
|
Column(u'description', UnicodeText),
|
||||||
|
Column(u'url', Unicode(255), nullable=False, index=True, unique=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "comments" table
|
||||||
|
comments_table = Table(u'comments', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'post_id', Integer, ForeignKey(u'posts.id'), nullable=True),
|
||||||
|
Column(u'user_id', Integer, ForeignKey(u'users.id'), nullable=False),
|
||||||
|
Column(u'title', Unicode(100), nullable=False),
|
||||||
|
Column(u'body', UnicodeText, nullable=False),
|
||||||
|
Column(u'status', Unicode(10), default='moderated'),
|
||||||
|
Column(u'created', DateTime, default=datetime.now()),
|
||||||
|
Column(u'modified', DateTime, default=datetime.now())
|
||||||
|
)
|
||||||
|
|
||||||
|
files_table = Table(u'files', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'user_id', Integer, ForeignKey(u'users.id'), nullable=False),
|
||||||
|
Column(u'media_type_id', Integer, ForeignKey(u'media_types.id'), nullable=False),
|
||||||
|
Column(u'filename', Unicode(255), nullable=False, index=True),
|
||||||
|
Column(u'mimetype', Unicode(255)),
|
||||||
|
Column(u'path', Unicode(255)),
|
||||||
|
Column(u'size', Integer, default=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
media_types_table = Table(u'media_types', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'title', Unicode(255), nullable=False, index=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "pages" table
|
||||||
|
pages_table = Table(u'pages', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'title', Unicode(255), nullable=False),
|
||||||
|
Column(u'body', UnicodeText),
|
||||||
|
Column(u'url', Unicode(255), nullable=False, index=True, unique=True),
|
||||||
|
Column(u'created', DateTime, default=datetime.now()),
|
||||||
|
Column(u'modified', DateTime, default=datetime.now())
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "permissions" table
|
||||||
|
permissions_table = Table(u'permissions', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'name', Unicode(80), nullable=False, index=True),
|
||||||
|
Column(u'description', UnicodeText)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "posts" table
|
||||||
|
posts_table = Table(u'posts', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'user_id', Integer, ForeignKey(u'users.id'), nullable=False),
|
||||||
|
Column(u'title', Unicode(255), nullable=False, index=True),
|
||||||
|
Column(u'body', UnicodeText, nullable=False, index=True),
|
||||||
|
Column(u'url', Unicode(255), nullable=False, index=True),
|
||||||
|
Column(u'status', Unicode(10), default=u'draft', index=True),
|
||||||
|
Column(u'comment_status', Unicode(10), default=u'open'),
|
||||||
|
Column(u'created', DateTime, default=datetime.now()),
|
||||||
|
Column(u'modified', DateTime, default=datetime.now())
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "roles" table
|
||||||
|
roles_table = Table(u'roles', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'name', Unicode(80), nullable=False, index=True),
|
||||||
|
Column(u'description', UnicodeText)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "tags" table
|
||||||
|
tags_table = Table(u'tags', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'name', Unicode(100), nullable=False),
|
||||||
|
Column(u'url', Unicode(255), nullable=False, index=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "users" table
|
||||||
|
users_table = Table(u'users', metadata,
|
||||||
|
Column(u'id', Integer, primary_key=True),
|
||||||
|
Column(u'email', Unicode(200), nullable=False, index=True),
|
||||||
|
Column(u'password', Unicode(64), nullable=False),
|
||||||
|
Column(u'nick', Unicode(50), nullable=False, index=True),
|
||||||
|
Column(u'first_name', Unicode(100), default=u''),
|
||||||
|
Column(u'last_name', Unicode(100), default=u''),
|
||||||
|
Column(u'homepage', Unicode(200), default=u''),
|
||||||
|
Column(u'timezone', Unicode(200), default=u'UTC'),
|
||||||
|
Column(u'activation_key', Unicode(40), default=None)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "variables" table
|
||||||
|
variables_table = Table(u'variables', metadata,
|
||||||
|
Column(u'key', Unicode(100), primary_key=True, index=True),
|
||||||
|
Column(u'value', Unicode(100), nullable=False),
|
||||||
|
Column(u'type', Unicode(10), default=u'string')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "categories_posts" table
|
||||||
|
categories_posts_table = Table(u'categories_posts', metadata,
|
||||||
|
Column(u'category_id', Integer, ForeignKey(u'categories.id'), primary_key=True),
|
||||||
|
Column(u'post_id', Integer, ForeignKey(u'posts.id'), primary_key=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "permissions_roles" bridging table
|
||||||
|
permissions_roles_table = Table(u'permissions_roles', metadata,
|
||||||
|
Column(u'permission_id', Integer, ForeignKey(u'permissions.id'), primary_key=True),
|
||||||
|
Column(u'role_id', Integer, ForeignKey(u'roles.id'), primary_key=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "posts_tags" table
|
||||||
|
posts_tags_table = Table(u'posts_tags', metadata,
|
||||||
|
Column(u'post_id', Integer, ForeignKey(u'posts.id'), primary_key=True),
|
||||||
|
Column(u'tag_id', Integer, ForeignKey(u'tags.id'), primary_key=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "roles_users" bridging table
|
||||||
|
roles_users_table = Table(u'roles_users', metadata,
|
||||||
|
Column(u'user_id', Integer, ForeignKey(u'users.id'), primary_key=True),
|
||||||
|
Column(u'role_id', Integer, ForeignKey(u'roles.id'), primary_key=True)
|
||||||
|
)
|
@ -1,337 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
###############################################################################
|
|
||||||
# ScribeEngine - Open Source Content Management System #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2010-2021 Raoul Snyman #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This file is part of ScribeEngine. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is free software: you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
|
||||||
# any later version. #
|
|
||||||
# #
|
|
||||||
# ScribeEngine is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with ScribeEngine. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
The :mod:`~scribeengine.models` module contains all the database models
|
|
||||||
"""
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from box import Box
|
|
||||||
from flask_user import UserMixin
|
|
||||||
|
|
||||||
from scribeengine.db import Model, Table, Column, ForeignKey, String, Text, Integer, Boolean, DateTime, \
|
|
||||||
backref, inspect, relationship
|
|
||||||
|
|
||||||
|
|
||||||
permissions_roles = Table(
|
|
||||||
'permissions_roles',
|
|
||||||
Column('permission_id', Integer, ForeignKey('permissions.id'), primary_key=True),
|
|
||||||
Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
nodes_taxonomies = Table(
|
|
||||||
'nodes_terms',
|
|
||||||
Column('node_id', Integer, ForeignKey('nod.id'), primary_key=True),
|
|
||||||
Column('taxonomy_id', Integer, ForeignKey('taxonomy.id'), primary_key=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
roles_users = Table(
|
|
||||||
'roles_users',
|
|
||||||
Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
|
|
||||||
Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DictMixin(object):
|
|
||||||
"""
|
|
||||||
A mixin class to add a "to_dict" method to each model
|
|
||||||
"""
|
|
||||||
def to_dict(self):
|
|
||||||
return Box({c.key: getattr(self, c.key) for c in inspect(self).mapper.column_attrs})
|
|
||||||
|
|
||||||
|
|
||||||
class Taxonomy(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
This is a grouping of related terms, like a tags or categories
|
|
||||||
"""
|
|
||||||
__tablename__ = 'taxonomies'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String(100), nullable=False)
|
|
||||||
description = Column(Text)
|
|
||||||
slug = Column(String(255), nullable=False, index=True, unique=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Node(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
Nodes are the basic content type
|
|
||||||
"""
|
|
||||||
__tablename__ = 'nodes'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
slug = Column(String(255), nullable=False, index=True, unique=True)
|
|
||||||
type = Column(String(255), nullable=False, index=True)
|
|
||||||
created = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
revision_id = Column(Integer, ForeignKey('revisions.id'), nullable=False, index=True)
|
|
||||||
|
|
||||||
current_revision = relationship('Revision', backref='node')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def complete(self):
|
|
||||||
"""Return a "full" or "complete" node, with all fields and data"""
|
|
||||||
node_dict = self.to_dict()
|
|
||||||
for field in self.fields:
|
|
||||||
node_dict[field.name] = Box({
|
|
||||||
'name': field.name,
|
|
||||||
'type': field.type,
|
|
||||||
'title': field.field.title,
|
|
||||||
'data': field.data
|
|
||||||
})
|
|
||||||
return node_dict
|
|
||||||
|
|
||||||
|
|
||||||
class Field(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A field is a model for field types on nodes
|
|
||||||
"""
|
|
||||||
__tablename__ = 'fields'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String(255), nullable=False, unique=True, index=True)
|
|
||||||
type = Column(String(255), nullable=False)
|
|
||||||
title = Column(String(255), nullable=False)
|
|
||||||
description = Column(Text)
|
|
||||||
created = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
|
|
||||||
|
|
||||||
class FieldRevision(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A revision of a field on a node
|
|
||||||
"""
|
|
||||||
__tablename__ = 'field_revisions'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
version = Column(Integer, nullable=False)
|
|
||||||
title = Column(String(255), nullable=False)
|
|
||||||
body = Column(Text)
|
|
||||||
format = Column(Text, nullable=False)
|
|
||||||
slug = Column(String(255), nullable=False)
|
|
||||||
created = Column(DateTime, nullable=False, index=True, default=datetime.utcnow)
|
|
||||||
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeField(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A node field is a field on a particular node
|
|
||||||
"""
|
|
||||||
__tablename__ = 'node_fields'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
field_id = Column(Integer, ForeignKey('fields.id'))
|
|
||||||
node_id = Column(Integer, ForeignKey('nodes.id'))
|
|
||||||
revision_id = Column(Integer, ForeignKey('field_revisions.id'), nullable=False, index=True)
|
|
||||||
title = Column(String(255), nullable=False)
|
|
||||||
description = Column(Text)
|
|
||||||
created = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
modified = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
||||||
|
|
||||||
current_revision = relationship('FieldRevision', backref='node_field')
|
|
||||||
field = relationship('Field', backref='node_field')
|
|
||||||
|
|
||||||
|
|
||||||
class File(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A file in the media library.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'files'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
|
|
||||||
media_type_id = Column(Integer, ForeignKey('media_types.id'), nullable=False)
|
|
||||||
filename = Column(String(255), nullable=False, index=True)
|
|
||||||
mimetype = Column(String(255))
|
|
||||||
path = Column(String(255))
|
|
||||||
size = Column(Integer, default=0)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.filename
|
|
||||||
|
|
||||||
|
|
||||||
class MediaType(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
Distinguishes between different types of media.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'media_types'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
title = Column(String(255), nullable=False, index=True)
|
|
||||||
|
|
||||||
files = relationship('File', backref='media_type')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
|
|
||||||
class Permission(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A single permission.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'permissions'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String(80), nullable=False, index=True)
|
|
||||||
description = Column(Text)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Role(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A role defines a set of permissions.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'roles'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String(80), nullable=False, index=True)
|
|
||||||
description = Column(Text)
|
|
||||||
|
|
||||||
permissions = relationship('Permission', backref='roles', secondary=permissions_roles)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Term(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A term is an item in a taxonomy. A term can be a category name, or a tag in a blog post.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'terms'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String(100), nullable=False)
|
|
||||||
slug = Column(String(255), nullable=False, index=True)
|
|
||||||
taxonomy_id = Column(Integer, ForeignKey('taxonomies.id'))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class User(Model, UserMixin, DictMixin):
|
|
||||||
"""
|
|
||||||
The user.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'users'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
email = Column(String(200), nullable=False, unique=True, index=True)
|
|
||||||
username = Column(String(200), nullable=False, unique=True, index=True)
|
|
||||||
password = Column(String(64), nullable=False)
|
|
||||||
nick = Column(String(50), nullable=False, index=True)
|
|
||||||
first_name = Column(String(100), default='')
|
|
||||||
last_name = Column(String(100), default='')
|
|
||||||
homepage = Column(String(200), default='')
|
|
||||||
timezone = Column(String(200), default='UTC')
|
|
||||||
activation_key = Column(String(40), default=None)
|
|
||||||
confirmed_at = Column(DateTime)
|
|
||||||
active = Column('is_active', Boolean, nullable=False, server_default='0')
|
|
||||||
|
|
||||||
comments = relationship('Comment', backref='user')
|
|
||||||
files = relationship('File', backref='user')
|
|
||||||
posts = relationship('Post', backref='author')
|
|
||||||
roles = relationship('Role', backref='users', secondary=roles_users)
|
|
||||||
|
|
||||||
def has_permission(self, permission):
|
|
||||||
if isinstance(permission, str):
|
|
||||||
for role in self.roles:
|
|
||||||
for perm in role.permissions:
|
|
||||||
if perm.name == permission:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
elif isinstance(permission, Permission):
|
|
||||||
for role in self.roles:
|
|
||||||
for perm in role.permissions:
|
|
||||||
if perm == permission:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
elif isinstance(permission, list):
|
|
||||||
for role in self.roles:
|
|
||||||
for perm in role.permissions:
|
|
||||||
if perm.name in permission:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(id(self))
|
|
||||||
|
|
||||||
|
|
||||||
class Variable(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
System variables.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'variables'
|
|
||||||
|
|
||||||
key = Column(String, primary_key=True, index=True)
|
|
||||||
value = Column(Text, nullable=False)
|
|
||||||
type = Column(String, default='string')
|
|
||||||
|
|
||||||
|
|
||||||
class Site(object):
|
|
||||||
"""
|
|
||||||
Generic model to hold the site-specfic data
|
|
||||||
"""
|
|
||||||
name = None
|
|
||||||
slogan = None
|
|
||||||
about = None
|
|
||||||
|
|
||||||
def __init__(self, name, slogan=None, about=None):
|
|
||||||
self.name = name
|
|
||||||
self.slogan = slogan
|
|
||||||
self.about = about
|
|
||||||
|
|
||||||
|
|
||||||
class Menu(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A menu
|
|
||||||
"""
|
|
||||||
__tablename__ = 'menus'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String, nullable=False)
|
|
||||||
description = Column(Text)
|
|
||||||
block = Column(String)
|
|
||||||
|
|
||||||
items = relationship('MenuItem', backref='menu')
|
|
||||||
|
|
||||||
|
|
||||||
class MenuItem(Model, DictMixin):
|
|
||||||
"""
|
|
||||||
A menu item
|
|
||||||
"""
|
|
||||||
__tablename__ = 'menu_items'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
title = Column(String, nullable=False)
|
|
||||||
url = Column(String, nullable=False)
|
|
||||||
weight = Column(Integer, default=0)
|
|
||||||
menu_id = Column(Integer, ForeignKey('menus.id'))
|
|
||||||
parent_id = Column(Integer, ForeignKey('menu_items.id'))
|
|
||||||
|
|
||||||
items = relationship('MenuItem', backref=backref('parent', remote_side=[id]))
|
|
BIN
scribeengine/public/images/file-image.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
scribeengine/public/images/file-unknown.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
scribeengine/public/images/img01.gif
Normal file
After Width: | Height: | Size: 399 B |
BIN
scribeengine/public/images/img02.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
scribeengine/public/images/img03.jpg
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
scribeengine/public/images/img04.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
scribeengine/public/images/img05.gif
Normal file
After Width: | Height: | Size: 810 B |
BIN
scribeengine/public/images/img06.gif
Normal file
After Width: | Height: | Size: 667 B |
BIN
scribeengine/public/images/img07.gif
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
scribeengine/public/images/media-button.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
scribeengine/public/images/minus_small.png
Normal file
After Width: | Height: | Size: 560 B |
BIN
scribeengine/public/images/spacer.gif
Normal file
After Width: | Height: | Size: 43 B |
BIN
scribeengine/public/pylons-logo.gif
Normal file
After Width: | Height: | Size: 2.3 KiB |
26
scribeengine/public/scripts/ScribeEngine.Blog.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* ScribeEngine - Open Source Blog Software *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright (c) 2010 Raoul Snyman *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it *
|
||||||
|
* under the terms of the GNU General Public License as published by the *
|
||||||
|
* Free Software Foundation; version 2 of the License. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||||
|
* Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along *
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.Blog", {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
ScribeEngine.Events.load(function () {
|
||||||
|
ScribeEngine.Widgets.elastic("#commentform textarea");
|
||||||
|
});
|
22
scribeengine/public/scripts/ScribeEngine.Init.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* ScribeEngine - Open Source Blog Software *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright (c) 2010 Raoul Snyman *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it *
|
||||||
|
* under the terms of the GNU General Public License as published by the *
|
||||||
|
* Free Software Foundation; version 2 of the License. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||||
|
* Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along *
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
ScribeEngine.Events.init();
|
||||||
|
});
|
98
scribeengine/public/scripts/ScribeEngine.Media.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* ScribeEngine - Open Source Blog Software *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright (c) 2010 Raoul Snyman *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it *
|
||||||
|
* under the terms of the GNU General Public License as published by the *
|
||||||
|
* Free Software Foundation; version 2 of the License. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||||
|
* Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along *
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.Media", {
|
||||||
|
init: function ()
|
||||||
|
{
|
||||||
|
// do nothing for the moment. in future, pre-select the file.
|
||||||
|
},
|
||||||
|
closeWindow: function ()
|
||||||
|
{
|
||||||
|
window.close();
|
||||||
|
},
|
||||||
|
newDirectory: function ()
|
||||||
|
{
|
||||||
|
var dirname = prompt("New Directory:", "directory");
|
||||||
|
},
|
||||||
|
getFiles: function (event)
|
||||||
|
{
|
||||||
|
var tree = jQuery.jstree._focused();
|
||||||
|
var path = tree.get_path(ScribeEngine.Events.getElement(event));
|
||||||
|
$("#selected-path").val(path);
|
||||||
|
$.getJSON(
|
||||||
|
'/media/get-files',
|
||||||
|
{"path": path.toString()},
|
||||||
|
function (data, textStatus) {
|
||||||
|
$("#file-list > ul").html("");
|
||||||
|
$.each(data.results, function () {
|
||||||
|
var file = this;
|
||||||
|
if (file.name.length > 15)
|
||||||
|
{
|
||||||
|
file.display_name = file.name.substr(0, 12) + "...";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file.display_name = file.name;
|
||||||
|
}
|
||||||
|
$("#file-list > ul").append(
|
||||||
|
$("<li>")
|
||||||
|
.append(
|
||||||
|
$("<a>").attr("href", "#").attr("class", "file")
|
||||||
|
.html("<img src=\""+ file.type +"\" />")
|
||||||
|
)
|
||||||
|
.append(
|
||||||
|
$("<a>").attr("href", "#").attr("class", "caption")
|
||||||
|
.attr("title", file.name).text(file.display_name)
|
||||||
|
)
|
||||||
|
.attr("title", file.name)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
selectFile: function (event)
|
||||||
|
{
|
||||||
|
var li = ScribeEngine.Events.getElement(event).parent();
|
||||||
|
if (!li.is("li"))
|
||||||
|
{
|
||||||
|
li = li.parent();
|
||||||
|
}
|
||||||
|
li.parent().children("li").children(".selected").removeClass("selected");
|
||||||
|
li.children("a").addClass("selected").blur();
|
||||||
|
$("#selected-file").val(li.attr("title"));
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
finishSelect: function (event)
|
||||||
|
{
|
||||||
|
var file = $("#selected-file").val();
|
||||||
|
var path = "/files/" + $("#selected-path").val().replace(",", "/");
|
||||||
|
var win = tinyMCEPopup.getWindowArg("window");
|
||||||
|
win.document.getElementById(tinyMCEPopup.getWindowArg("input")).value = path + "/" + file;
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
//window.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ScribeEngine.Events.load(function () {
|
||||||
|
ScribeEngine.Events.click("#new-directory", ScribeEngine.Media.newDirectory);
|
||||||
|
ScribeEngine.Events.click("#file-select", ScribeEngine.Media.finishSelect);
|
||||||
|
ScribeEngine.Events.liveClick("#file-list > ul > li > a", ScribeEngine.Media.selectFile);
|
||||||
|
tinyMCEPopup.onInit.add(ScribeEngine.Media.init, ScribeEngine.Media);
|
||||||
|
});
|
27
scribeengine/public/scripts/ScribeEngine.Post.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* ScribeEngine - Open Source Blog Software *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright (c) 2010 Raoul Snyman *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it *
|
||||||
|
* under the terms of the GNU General Public License as published by the *
|
||||||
|
* Free Software Foundation; version 2 of the License. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||||
|
* Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along *
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.Post", {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
ScribeEngine.Events.load(function () {
|
||||||
|
ScribeEngine.Widgets.tagEditor("#post-tags");
|
||||||
|
ScribeEngine.Widgets.tinymce("textarea");
|
||||||
|
});
|
298
scribeengine/public/scripts/ScribeEngine.js
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* ScribeEngine - Open Source Blog Software *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright (c) 2010 Raoul Snyman *
|
||||||
|
* ------------------------------------------------------------------------- *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it *
|
||||||
|
* under the terms of the GNU General Public License as published by the *
|
||||||
|
* Free Software Foundation; version 2 of the License. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||||
|
* Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along *
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
window["ScribeEngine"] = {
|
||||||
|
Namespace: {
|
||||||
|
/**
|
||||||
|
* Create a Javascript namespace.
|
||||||
|
* Based on: http://code.google.com/p/namespacedotjs/
|
||||||
|
* Idea behind this is to created nested namespaces that are not ugly.
|
||||||
|
* Example:
|
||||||
|
* Namespace(foo.bar);
|
||||||
|
* foo.bar..myFunction = function () { } ;
|
||||||
|
*/
|
||||||
|
create: function (name, attributes) {
|
||||||
|
var parts = name.split('.'),
|
||||||
|
ns = window,
|
||||||
|
i = 0;
|
||||||
|
// find the deepest part of the namespace
|
||||||
|
// that is already defined
|
||||||
|
for(; i < parts.length && parts[i] in ns; i++)
|
||||||
|
ns = ns[parts[i]];
|
||||||
|
// initialize any remaining parts of the namespace
|
||||||
|
for(; i < parts.length; i++)
|
||||||
|
ns = ns[parts[i]] = {};
|
||||||
|
// copy the attributes into the namespace
|
||||||
|
for (var attr in attributes)
|
||||||
|
ns[attr] = attributes[attr];
|
||||||
|
},
|
||||||
|
exists: function (namespace) {
|
||||||
|
/**
|
||||||
|
* Determine the namespace of a page
|
||||||
|
*/
|
||||||
|
page_namespace = $ScribeEngine.Namespace.get_page_namespace();
|
||||||
|
return (namespace == page_namespace);
|
||||||
|
},
|
||||||
|
get_page_namespace: function () {
|
||||||
|
return $("#content > h2").attr("id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.append = function (elem) {
|
||||||
|
this[this.length] = elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.Events", {
|
||||||
|
// Local variables
|
||||||
|
onload_functions: Array(),
|
||||||
|
// Functions
|
||||||
|
load: function (func) {
|
||||||
|
this.onload_functions.append(func);
|
||||||
|
},
|
||||||
|
click: function (selector, func) {
|
||||||
|
$(selector).bind("click", func);
|
||||||
|
},
|
||||||
|
change: function (selector, func) {
|
||||||
|
$(selector).bind("change", func);
|
||||||
|
},
|
||||||
|
submit: function (selector, func) {
|
||||||
|
$(selector).bind("submit", func);
|
||||||
|
},
|
||||||
|
blur: function (selector, func) {
|
||||||
|
$(selector).bind("blur", func);
|
||||||
|
},
|
||||||
|
paste: function (selector, func) {
|
||||||
|
$(selector).bind("paste", func);
|
||||||
|
},
|
||||||
|
keyup: function (selector, func) {
|
||||||
|
$(selector).bind("keyup", func);
|
||||||
|
},
|
||||||
|
keydown: function (selector, func) {
|
||||||
|
$(selector).bind("keydown", func);
|
||||||
|
},
|
||||||
|
keypress: function (selector, func) {
|
||||||
|
$(selector).bind("keypress", func);
|
||||||
|
},
|
||||||
|
liveClick: function (selector, func) {
|
||||||
|
$(selector).live("click", func);
|
||||||
|
},
|
||||||
|
getElement: function(event) {
|
||||||
|
var targ;
|
||||||
|
if (!event) {
|
||||||
|
var event = window.event;
|
||||||
|
}
|
||||||
|
if (event.target) {
|
||||||
|
targ = event.target;
|
||||||
|
}
|
||||||
|
else if (event.srcElement) {
|
||||||
|
targ = event.srcElement;
|
||||||
|
}
|
||||||
|
if (targ.nodeType == 3) {
|
||||||
|
// defeat Safari bug
|
||||||
|
targ = targ.parentNode;
|
||||||
|
}
|
||||||
|
return $(targ);
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
for (idx in this.onload_functions) {
|
||||||
|
func = this.onload_functions[idx];
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.Widgets", {
|
||||||
|
/**
|
||||||
|
* Adds a datepicker to an element.
|
||||||
|
*/
|
||||||
|
datepicker: function (selector)
|
||||||
|
{
|
||||||
|
$(selector).datepicker({showButtonPanel: true, dateFormat: "dd/mm/yy"});
|
||||||
|
},
|
||||||
|
tagEditor: function (selector)
|
||||||
|
{
|
||||||
|
$(selector).tagEditor({completeOnBlur: true, initialParse: true});
|
||||||
|
},
|
||||||
|
elastic: function (selector)
|
||||||
|
{
|
||||||
|
$(selector).elastic();
|
||||||
|
},
|
||||||
|
fileBrowser: function (field_name, url, type, win)
|
||||||
|
{
|
||||||
|
//alert("Field_Name: " + field_name + "\nURL: " + url + "\nType: " + type + "\nWin: " + win); // debug/testing
|
||||||
|
var cmsURL = window.location.toString(); // script URL - use an absolute path!
|
||||||
|
if (cmsURL.indexOf("?") < 0)
|
||||||
|
{
|
||||||
|
//add the type as the only query parameter
|
||||||
|
cmsURL = cmsURL + "?type=" + type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//add the type as an additional query parameter
|
||||||
|
// (PHP session ID is now included if there is one at all)
|
||||||
|
cmsURL = cmsURL + "&type=" + type;
|
||||||
|
}
|
||||||
|
tinyMCE.activeEditor.windowManager.open(
|
||||||
|
{
|
||||||
|
file: "/media",
|
||||||
|
title: "Media Library",
|
||||||
|
width: 600, // Your dimensions may differ - toy around with them!
|
||||||
|
height: 400,
|
||||||
|
resizable: "yes",
|
||||||
|
inline: "yes", // This parameter only has an effect if you use the inlinepopups plugin!
|
||||||
|
close_previous: "no"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
window: win,
|
||||||
|
input: field_name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
tinymce: function (selector)
|
||||||
|
{
|
||||||
|
$(selector).tinymce({
|
||||||
|
script_url: "/scripts/tinymce/tiny_mce.js",
|
||||||
|
theme: "advanced",
|
||||||
|
dialog_type: "modal",
|
||||||
|
theme_advanced_toolbar_location: "top",
|
||||||
|
theme_advanced_toolbar_align: "left",
|
||||||
|
theme_advanced_statusbar_location: "bottom",
|
||||||
|
theme_advanced_resizing: true,
|
||||||
|
theme_advanced_resize_horizontal: false,
|
||||||
|
file_browser_callback: "ScribeEngine.Widgets.fileBrowser"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
tree: function (selector, tree_data, onselect)
|
||||||
|
{
|
||||||
|
$(selector)
|
||||||
|
.bind("click.jstree", onselect)
|
||||||
|
.jstree({
|
||||||
|
plugins: ["themes", "json_data"],
|
||||||
|
json_data: {data: tree_data},
|
||||||
|
themes: {theme: "default"}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ScribeEngine.Namespace.create("ScribeEngine.General", {
|
||||||
|
/**
|
||||||
|
* Fades out a message
|
||||||
|
*/
|
||||||
|
fade_message: function ()
|
||||||
|
{
|
||||||
|
$("#message").hide().fadeIn("slow", function() {
|
||||||
|
setTimeout("$('#message').fadeOut('slow');", 1500);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Checks for a message and fades it in and out.
|
||||||
|
*/
|
||||||
|
show_message: function ()
|
||||||
|
{
|
||||||
|
if ($("#message"))
|
||||||
|
{
|
||||||
|
setTimeout("$ScribeEngine.General.fade_message()", 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Dynamically hide anything on the page that has a "jshidden" class.
|
||||||
|
*/
|
||||||
|
hide_elements: function ()
|
||||||
|
{
|
||||||
|
$(".jshidden").hide();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Go to a particular month or year on the calendar.
|
||||||
|
*/
|
||||||
|
go_to_month: function (e)
|
||||||
|
{
|
||||||
|
var link = ScribeEngine.Events.getElement(e);
|
||||||
|
$("#calendar_wrap").load(link.attr("href"), function() {
|
||||||
|
ScribeEngine.Events.click("#next a", ScribeEngine.General.go_to_month);
|
||||||
|
ScribeEngine.Events.click("#prev a", ScribeEngine.General.go_to_month);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Do all the funny things required to toggle a fieldset
|
||||||
|
*/
|
||||||
|
perform_toggle: function (fieldset)
|
||||||
|
{
|
||||||
|
content = $('> div', fieldset);
|
||||||
|
if (fieldset.is('.collapsed'))
|
||||||
|
{
|
||||||
|
fieldset.removeClass('collapsed');
|
||||||
|
content.slideDown('normal');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
content.slideUp('normal',
|
||||||
|
function()
|
||||||
|
{
|
||||||
|
fieldset.addClass('collapsed');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Find the fieldset to toggle, and then perform the toggle
|
||||||
|
*/
|
||||||
|
toggle_fieldset: function ()
|
||||||
|
{
|
||||||
|
fieldset = $(this).parent().parent();
|
||||||
|
$ScribeEngine.General.perform_toggle(fieldset);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Initialises collapsible fieldsets
|
||||||
|
*/
|
||||||
|
init_fieldsets: function ()
|
||||||
|
{
|
||||||
|
$("fieldset.collapsible > legend").each(function() {
|
||||||
|
legend = $(this);
|
||||||
|
legend_text = legend.text();
|
||||||
|
legend.text("");
|
||||||
|
legend.append(
|
||||||
|
$("<a>").attr("href", "#").attr("title", "Expand/collapse details")
|
||||||
|
.text(legend_text).click($ScribeEngine.General.toggle_fieldset));
|
||||||
|
});
|
||||||
|
$("fieldset.collapsed").each(function() {
|
||||||
|
$("> div.content", this).slideUp("normal");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Initialises elastic textareas
|
||||||
|
*/
|
||||||
|
init_textareas: function ()
|
||||||
|
{
|
||||||
|
$("textarea").elastic();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global onload
|
||||||
|
*
|
||||||
|
* This function below will be executed on all page views.
|
||||||
|
*/
|
||||||
|
ScribeEngine.Events.load(function () {
|
||||||
|
ScribeEngine.Events.click("#next a", ScribeEngine.General.go_to_month);
|
||||||
|
ScribeEngine.Events.click("#prev a", ScribeEngine.General.go_to_month);
|
||||||
|
});
|
1
scribeengine/public/scripts/jquery.elastic.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(jQuery){jQuery.fn.extend({elastic:function(){var mimics=['paddingTop','paddingRight','paddingBottom','paddingLeft','fontSize','lineHeight','fontFamily','width','fontWeight'];return this.each(function(){if(this.type!='textarea'){return false}var $textarea=jQuery(this),$twin=jQuery('<div />').css({'position':'absolute','display':'none','word-wrap':'break-word'}),lineHeight=parseInt($textarea.css('line-height'),10)||parseInt($textarea.css('font-size'),'10'),minheight=parseInt($textarea.css('height'),10)||lineHeight*3,maxheight=parseInt($textarea.css('max-height'),10)||Number.MAX_VALUE,goalheight=0,i=0;if(maxheight<0){maxheight=Number.MAX_VALUE}$twin.appendTo($textarea.parent());var i=mimics.length;while(i--){$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()))}function setHeightAndOverflow(height,overflow){curratedHeight=Math.floor(parseInt(height,10));if($textarea.height()!=curratedHeight){$textarea.css({'height':curratedHeight+'px','overflow':overflow})}}function update(){var textareaContent=$textarea.val().replace(/&/g,'&').replace(/ /g,' ').replace(/<|>/g,'>').replace(/\n/g,'<br />');var twinContent=$twin.html();if(textareaContent+' '!=twinContent){$twin.html(textareaContent+' ');if(Math.abs($twin.height()+lineHeight-$textarea.height())>3){var goalheight=$twin.height()+lineHeight;if(goalheight>=maxheight){setHeightAndOverflow(maxheight,'auto')}else if(goalheight<=minheight){setHeightAndOverflow(minheight,'hidden')}else{setHeightAndOverflow(goalheight,'hidden')}}}}$textarea.css({'overflow':'hidden'});$textarea.keyup(function(){update()});$textarea.live('input paste',function(e){setTimeout(update,250)});update()})}})})(jQuery);
|
6240
scribeengine/public/scripts/jquery.js
vendored
Normal file
122
scribeengine/public/scripts/jquery.metadata.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Metadata - jQuery plugin for parsing metadata from elements
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 John Resig, Yehuda Katz, J<EFBFBD>örn Zaefferer, Paul McLanahan
|
||||||
|
*
|
||||||
|
* Dual licensed under the MIT and GPL licenses:
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
|
*
|
||||||
|
* Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type of metadata to use. Metadata is encoded in JSON, and each property
|
||||||
|
* in the JSON will become a property of the element itself.
|
||||||
|
*
|
||||||
|
* There are three supported types of metadata storage:
|
||||||
|
*
|
||||||
|
* attr: Inside an attribute. The name parameter indicates *which* attribute.
|
||||||
|
*
|
||||||
|
* class: Inside the class attribute, wrapped in curly braces: { }
|
||||||
|
*
|
||||||
|
* elem: Inside a child element (e.g. a script tag). The
|
||||||
|
* name parameter indicates *which* element.
|
||||||
|
*
|
||||||
|
* The metadata for an element is loaded the first time the element is accessed via jQuery.
|
||||||
|
*
|
||||||
|
* As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
|
||||||
|
* matched by expr, then redefine the metadata type and run another $(expr) for other elements.
|
||||||
|
*
|
||||||
|
* @name $.metadata.setType
|
||||||
|
*
|
||||||
|
* @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
|
||||||
|
* @before $.metadata.setType("class")
|
||||||
|
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||||
|
* @desc Reads metadata from the class attribute
|
||||||
|
*
|
||||||
|
* @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
|
||||||
|
* @before $.metadata.setType("attr", "data")
|
||||||
|
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||||
|
* @desc Reads metadata from a "data" attribute
|
||||||
|
*
|
||||||
|
* @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
|
||||||
|
* @before $.metadata.setType("elem", "script")
|
||||||
|
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
|
||||||
|
* @desc Reads metadata from a nested script element
|
||||||
|
*
|
||||||
|
* @param String type The encoding type
|
||||||
|
* @param String name The name of the attribute to be used to get metadata (optional)
|
||||||
|
* @cat Plugins/Metadata
|
||||||
|
* @descr Sets the type of encoding to be used when loading metadata for the first time
|
||||||
|
* @type undefined
|
||||||
|
* @see metadata()
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
$.extend({
|
||||||
|
metadata : {
|
||||||
|
defaults : {
|
||||||
|
type: 'class',
|
||||||
|
name: 'metadata',
|
||||||
|
cre: /({.*})/,
|
||||||
|
single: 'metadata'
|
||||||
|
},
|
||||||
|
setType: function( type, name ){
|
||||||
|
this.defaults.type = type;
|
||||||
|
this.defaults.name = name;
|
||||||
|
},
|
||||||
|
get: function( elem, opts ){
|
||||||
|
var settings = $.extend({},this.defaults,opts);
|
||||||
|
// check for empty string in single property
|
||||||
|
if ( !settings.single.length ) settings.single = 'metadata';
|
||||||
|
|
||||||
|
var data = $.data(elem, settings.single);
|
||||||
|
// returned cached data if it already exists
|
||||||
|
if ( data ) return data;
|
||||||
|
|
||||||
|
data = "{}";
|
||||||
|
|
||||||
|
if ( settings.type == "class" ) {
|
||||||
|
var m = settings.cre.exec( elem.className );
|
||||||
|
if ( m )
|
||||||
|
data = m[1];
|
||||||
|
} else if ( settings.type == "elem" ) {
|
||||||
|
if( !elem.getElementsByTagName )
|
||||||
|
return undefined;
|
||||||
|
var e = elem.getElementsByTagName(settings.name);
|
||||||
|
if ( e.length )
|
||||||
|
data = $.trim(e[0].innerHTML);
|
||||||
|
} else if ( elem.getAttribute != undefined ) {
|
||||||
|
var attr = elem.getAttribute( settings.name );
|
||||||
|
if ( attr )
|
||||||
|
data = attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data.indexOf( '{' ) <0 )
|
||||||
|
data = "{" + data + "}";
|
||||||
|
|
||||||
|
data = eval("(" + data + ")");
|
||||||
|
|
||||||
|
$.data( elem, settings.single, data );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the metadata object for the first member of the jQuery object.
|
||||||
|
*
|
||||||
|
* @name metadata
|
||||||
|
* @descr Returns element's metadata object
|
||||||
|
* @param Object opts An object contianing settings to override the defaults
|
||||||
|
* @type jQuery
|
||||||
|
* @cat Plugins/Metadata
|
||||||
|
*/
|
||||||
|
$.fn.metadata = function( opts ){
|
||||||
|
return $.metadata.get( this[0], opts );
|
||||||
|
};
|
||||||
|
|
||||||
|
})(jQuery);
|
114
scribeengine/public/scripts/jquery.tag.editor.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
@author: Karl-Johan Sjögren / http://blog.crazybeavers.se/
|
||||||
|
@url: http://blog.crazybeavers.se/wp-content/demos/jquery.tag.editor/
|
||||||
|
@license: Creative Commons License - ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
@version: 1.1
|
||||||
|
*/
|
||||||
|
(function($) {
|
||||||
|
$.fn.extend({
|
||||||
|
tagEditor: function(options) {
|
||||||
|
var defaults = {
|
||||||
|
separator: ',',
|
||||||
|
items: [],
|
||||||
|
className: 'tagEditor',
|
||||||
|
confirmRemoval: false,
|
||||||
|
completeOnSeparator: false,
|
||||||
|
completeOnBlur: false
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = $.extend(defaults, options);
|
||||||
|
var listBase, textBase = this, hiddenText;
|
||||||
|
var itemBase = [];
|
||||||
|
|
||||||
|
return this.each(function() {
|
||||||
|
textBase.id = options.formField;
|
||||||
|
|
||||||
|
hiddenText = $(document.createElement('input'));
|
||||||
|
hiddenText.attr('type', 'hidden');
|
||||||
|
textBase.after(hiddenText);
|
||||||
|
|
||||||
|
listBase = $(document.createElement('ul'));
|
||||||
|
listBase.attr('class', options.className);
|
||||||
|
$(this).after(listBase);
|
||||||
|
|
||||||
|
for (var i = 0; i < options.items.length; i++) {
|
||||||
|
addTag(jQuery.trim(options.items[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
buildArray();
|
||||||
|
$(this).keypress(handleKeys);
|
||||||
|
$(this).blur(parse);
|
||||||
|
|
||||||
|
var form = $(this).parents("form");
|
||||||
|
form.submit(function() {
|
||||||
|
parse();
|
||||||
|
hiddenText.val(itemBase.join(options.separator));
|
||||||
|
hiddenText.attr("id", textBase.attr("id"));
|
||||||
|
hiddenText.attr("name", textBase.attr("name"));
|
||||||
|
textBase.attr("id", textBase.attr("id") + '_old');
|
||||||
|
textBase.attr("name", textBase.attr("name") + '_old');
|
||||||
|
});
|
||||||
|
|
||||||
|
function addTag(tag) {
|
||||||
|
tag = jQuery.trim(tag);
|
||||||
|
for (var i = 0; i < itemBase.length; i++) {
|
||||||
|
if (itemBase[i].toLowerCase() == tag.toLowerCase())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = $(document.createElement('li'));
|
||||||
|
item.text(tag);
|
||||||
|
item.attr('title', 'Remove tag');
|
||||||
|
item.click(function() {
|
||||||
|
if (options.confirmRemoval)
|
||||||
|
if (!confirm("Do you really want to remove the tag?"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
item.remove();
|
||||||
|
parse();
|
||||||
|
});
|
||||||
|
|
||||||
|
listBase.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildArray() {
|
||||||
|
itemBase = [];
|
||||||
|
var items = $("li", listBase);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
itemBase.push(jQuery.trim($(items[i]).text()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse() {
|
||||||
|
var items = textBase.val().split(options.separator);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
var trimmedItem = jQuery.trim(items[i]);
|
||||||
|
if (trimmedItem.length > 0)
|
||||||
|
addTag(trimmedItem);
|
||||||
|
}
|
||||||
|
textBase.val("");
|
||||||
|
buildArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeys(ev) {
|
||||||
|
if (options.completeOnSeparator) {
|
||||||
|
if (String.fromCharCode(ev.keyCode) == options.separator) {
|
||||||
|
parse();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ev.keyCode) {
|
||||||
|
case 13:
|
||||||
|
{
|
||||||
|
parse();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(jQuery);
|
1
scribeengine/public/scripts/jquery.tinymce.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(b){var e,d,a=[],c=window;b.fn.tinymce=function(j){var p=this,g,k,h,m,i,l="",n="";if(!p.length){return}if(!j){return tinyMCE.get(p[0].id)}function o(){var r=[],q=0;if(f){f();f=null}p.each(function(t,u){var s,w=u.id,v=j.oninit;if(!w){u.id=w=tinymce.DOM.uniqueId()}s=new tinymce.Editor(w,j);r.push(s);if(v){s.onInit.add(function(){var x,y=v;if(++q==r.length){if(tinymce.is(y,"string")){x=(y.indexOf(".")===-1)?null:tinymce.resolve(y.replace(/\.\w+$/,""));y=tinymce.resolve(y)}y.apply(x||tinymce,r)}})}});b.each(r,function(t,s){s.render()})}if(!c.tinymce&&!d&&(g=j.script_url)){d=1;h=g.substring(0,g.lastIndexOf("/"));if(/_(src|dev)\.js/g.test(g)){n="_src"}m=g.lastIndexOf("?");if(m){l=g.substring(m+1)}c.tinyMCEPreInit={base:h,suffix:n,query:l};if(g.indexOf("gzip")!=-1){i=j.language||"en";g=g+(/\?/.test(g)?"&":"?")+"js=true&core=true&suffix="+escape(n)+"&themes="+escape(j.theme)+"&plugins="+escape(j.plugins)+"&languages="+i;if(!c.tinyMCE_GZ){tinyMCE_GZ={start:function(){tinymce.suffix=n;function q(r){tinymce.ScriptLoader.markDone(tinyMCE.baseURI.toAbsolute(r))}q("langs/"+i+".js");q("themes/"+j.theme+"/editor_template"+n+".js");q("themes/"+j.theme+"/langs/"+i+".js");b.each(j.plugins.split(","),function(s,r){if(r){q("plugins/"+r+"/editor_plugin"+n+".js");q("plugins/"+r+"/langs/"+i+".js")}})},end:function(){}}}}b.ajax({type:"GET",url:g,dataType:"script",cache:true,success:function(){tinymce.dom.Event.domLoaded=1;d=2;o();b.each(a,function(q,r){r()})}})}else{if(d===1){a.push(o)}else{o()}}};b.extend(b.expr[":"],{tinymce:function(g){return g.id&&!!tinyMCE.get(g.id)}});function f(){function i(){this.find("span.mceEditor,div.mceEditor").each(function(m,n){var l=tinyMCE.get(n.id.replace(/_parent$/,""));if(l){l.remove()}})}function k(n){var m=this,l;if(n!==e){i.call(m);m.each(function(p,q){var o;if(o=tinyMCE.get(q.id)){o.setContent(n)}})}else{if(m.length>0){if(l=tinyMCE.get(m[0].id)){return l.getContent()}}}}function h(m){var l=null;(m)&&(m.id)&&(c.tinymce)&&(l=tinyMCE.get(m.id));return l}function g(l){return !!((l)&&(l.length)&&(c.tinymce)&&(l.is(":tinymce")))}var j={};b.each(["text","html","val"],function(n,l){var o=j[l]=b.fn[l],m=(l==="text");b.fn[l]=function(r){var p=this;if(!g(p)){return o.call(p,r)}if(r!==e){k.call(p.filter(":tinymce"),r);o.call(p.not(":tinymce"),r);return p}else{var q="";(m?p:p.eq(0)).each(function(t,u){var s=h(u);q+=s?(m?s.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):s.getContent()):o.call(b(u),r)});return q}}});b.each(["append","prepend"],function(n,m){var o=j[m]=b.fn[m],l=(m==="prepend");b.fn[m]=function(q){var p=this;if(!g(p)){return o.call(p,q)}if(q!==e){p.filter(":tinymce").each(function(s,t){var r=h(t);r&&r.setContent(l?q+r.getContent():r.getContent()+q)});o.call(p.not(":tinymce"),q);return p}}});b.each(["remove","replaceWith","replaceAll","empty"],function(m,l){var n=j[l]=b.fn[l];b.fn[l]=function(){i.apply(this);return n.apply(this,arguments)}});j.attr=b.fn.attr;b.fn.attr=function(n,q,o){var m=this;if((!n)||(n!=="value")||(!g(m))){return j.attr.call(m,n,q,o)}if(q!==e){k.call(m.filter(":tinymce"),q);j.attr.call(m.not(":tinymce"),n,q,o);return m}else{var p=m[0],l=h(p);return l?l.getContent():j.attr.call(b(p),n,q,o)}}}})(jQuery);
|
16
scribeengine/public/scripts/jquery.validate.js
vendored
Normal file
142
scribeengine/public/scripts/jstree/jquery.jstree.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
(function(c){c.vakata={};c.vakata.css={get_css:function(a,d,g){a=a.toLowerCase();var f=g.cssRules||g.rules,b=0;do{if(f.length&&b>f.length+5)return false;if(f[b].selectorText&&f[b].selectorText.toLowerCase()==a)if(d===true){g.removeRule&&g.removeRule(b);g.deleteRule&&g.deleteRule(b);return true}else return f[b]}while(f[++b]);return false},add_css:function(a,d){if(c.jstree.css.get_css(a,false,d))return false;d.insertRule?d.insertRule(a+" { }",0):d.addRule(a,null,0);return c.vakata.css.get_css(a)},remove_css:function(a,
|
||||||
|
d){return c.vakata.css.get_css(a,true,d)},add_sheet:function(a){var d;if(a.str){d=document.createElement("style");d.setAttribute("type","text/css");if(d.styleSheet){document.getElementsByTagName("head")[0].appendChild(d);d.styleSheet.cssText=a.str}else{d.appendChild(document.createTextNode(a.str));document.getElementsByTagName("head")[0].appendChild(d)}return d.sheet||d.styleSheet}if(a.url)if(document.createStyleSheet)try{document.createStyleSheet(a.url)}catch(g){}else{d=document.createElement("link");
|
||||||
|
d.rel="stylesheet";d.type="text/css";d.media="all";d.href=a.url;document.getElementsByTagName("head")[0].appendChild(d);return d.styleSheet}}}})(jQuery);
|
||||||
|
(function(c){var a=[],d=-1,g={},f={};c.fn.jstree=function(b){var e=typeof b=="string",h=Array.prototype.slice.call(arguments,1),k=this;!e&&c.meta&&h.push(c.metadata.get(this).jstree);b=!e&&h.length?c.extend.apply(null,[true,b].concat(h)):b;if(e&&b.substring(0,1)=="_")return k;e?this.each(function(){var j=a[c.data(this,"jstree-instance-id")];j=j&&c.isFunction(j[b])?j[b].apply(j,h):j;if(typeof j!=="undefined"&&j!==true&&j!==false){k=j;return false}}):this.each(function(){var j=c.data(this,"jstree-instance-id"),
|
||||||
|
i=false;j&&a[j]&&a[j].destroy();j=parseInt(a.push({}),10)-1;c.data(this,"jstree-instance-id",j);b.plugins=c.isArray(b.plugins)?b.plugins:c.jstree.defaults.plugins;c.inArray("core",b.plugins)===-1&&b.plugins.unshift("core");i=c.extend(true,{},c.jstree.defaults,b);i.plugins=b.plugins;a[j]=new c.jstree._instance(j,c(this).addClass("jstree jstree-"+j),i);c.each(a[j].get_settings().plugins,function(m,l){a[j].data[l]={}});c.each(a[j].get_settings().plugins,function(m,l){g[l]&&g[l].__init.apply(a[j])});
|
||||||
|
a[j].init()});return k};c.jstree={defaults:{plugins:[]},_focused:function(){return a[d]||null},_reference:function(b){if(a[b])return a[b];var e=c(b);if(!e.length&&typeof b==="string")e=c("#"+b);if(!e.length)return null;return a[e.closest(".jstree").data("jstree-instance-id")]||null},_instance:function(b,e,h){this.data={core:{}};this.get_settings=function(){return c.extend(true,{},h)};this.get_index=function(){return b};this.get_container=function(){return e};this._set_settings=function(k){h=c.extend(true,
|
||||||
|
{},h,k)}},_fn:{},plugin:function(b,e){e=c.extend({},{__init:c.noop,__destroy:c.noop,_fn:{},defaults:false},e);g[b]=e;c.jstree.defaults[b]=e.defaults;c.each(e._fn,function(h,k){k.plugin=b;k.old=c.jstree._fn[h];c.jstree._fn[h]=function(){var j,i=k,m=Array.prototype.slice.call(arguments);j=this.get_settings();var l=new c.Event("before.jstree"),o=false;do{if(i&&i.plugin&&c.inArray(i.plugin,j.plugins)!==-1)break;i=i.old}while(i);if(i){j=this.get_container().triggerHandler(l,{func:h,inst:this,args:m});
|
||||||
|
if(j!==false){if(typeof j!=="undefined")m=j;return j=i.apply(c.extend({},this,{__callback:function(n){this.get_container().triggerHandler(h+".jstree",{inst:this,args:m,rslt:n,rlbk:o})},__rollback:function(){return o=this.get_rollback()},__call_old:function(n){return i.old.apply(this,n?Array.prototype.slice.call(arguments,1):m)}}),m)}}};c.jstree._fn[h].old=k.old;c.jstree._fn[h].plugin=b})},rollback:function(b){if(b){c.isArray(b)||(b=[b]);c.each(b,function(e,h){a[h.i].set_rollback(h.h,h.d)})}}};c.jstree._fn=
|
||||||
|
c.jstree._instance.prototype={};c(function(){var b=navigator.userAgent.toLowerCase(),e=(b.match(/.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],h=".jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } .jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; } .jstree > ul > li { margin-left:0px; } .jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } .jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } .jstree a:focus { outline: none; } .jstree a > ins { height:16px; width:16px; } .jstree a > .jstree-icon { margin-right:3px; } li.jstree-open > ul { display:block; } li.jstree-closed > ul { display:none; } ";
|
||||||
|
if(/msie/.test(b)&&parseInt(e,10)==6)h+=".jstree li { height:18px; margin-left:0; } .jstree li li { margin-left:18px; } li.jstree-open ul { display:block; } li.jstree-closed ul { display:none !important; } .jstree li a { display:inline; } .jstree li a ins { height:16px; width:16px; margin-right:3px; } ";c.vakata.css.add_sheet({str:h})});c.jstree.plugin("core",{__init:function(){this.data.core.to_open=c.map(c.makeArray(this.get_settings().core.initially_open),function(b){return"#"+b.toString().replace(/^#/,
|
||||||
|
"").replace("\\/","/").replace("/","\\/")})},defaults:{html_titles:false,animation:500,initially_open:[]},_fn:{init:function(){this.set_focus();this.get_container().html("<ul><li class='jstree-last jstree-leaf'><ins> </ins><a class='jstree-loading' href='#'><ins class='jstree-icon'> </ins>Loading ...</a></li></ul>");this.data.core.li_height=this.get_container().find("ul li.jstree-closed, ul li.jstree-leaf").eq(0).height()||18;this.get_container().delegate("li > ins","click.jstree",c.proxy(function(b){var e=
|
||||||
|
c(b.target);e.is("ins")&&b.pageY-e.offset().top<this.data.core.li_height&&this.toggle_node(e)},this)).bind("mousedown.jstree",c.proxy(function(){this.set_focus()},this)).bind("dblclick.jstree",function(){var b;if(document.selection&&document.selection.empty)document.selection.empty();else if(window.getSelection){b=window.getSelection();try{b.removeAllRanges();b.collapse()}catch(e){}}});this.__callback();this.load_node(-1,function(){this.loaded();this.reopen()})},destroy:function(){var b,e=this.get_index(),
|
||||||
|
h=this.get_settings(),k=this;c.each(h.plugins,function(j,i){g[i].__destroy.apply(k)});this.__callback();if(this.is_focused())for(b in a)if(a.hasOwnProperty(b)&&b!=e){a[b].set_focus();break}if(e===d)d=-1;this.get_container().unbind(".jstree").undelegate(".jstree").removeData("jstree-instance-id").find("[class^='jstree']").andSelf().attr("class",function(){return this.className.replace(/jstree[^ ]*|$/ig,"")});a[e]=null;delete a[e]},save_opened:function(){var b=this;this.data.core.to_open=[];this.get_container().find(".jstree-open").each(function(){b.data.core.to_open.push("#"+
|
||||||
|
this.id.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/"))});this.__callback(b.data.core.to_open)},reopen:function(b){var e=this,h=true,k=[],j=[];if(!b){this.data.core.reopen=false;this.data.core.refreshing=true}if(this.data.core.to_open.length){c.each(this.data.core.to_open,function(i,m){if(m=="#")return true;c(m).length&&c(m).is(".jstree-closed")?k.push(m):j.push(m)});if(k.length){this.data.core.to_open=j;c.each(k,function(i,m){e.open_node(m,function(){e.reopen(true)},true)});h=
|
||||||
|
false}}if(h){this.data.core.reopen&&clearTimeout(this.data.core.reopen);this.data.core.reopen=setTimeout(function(){e.__callback({},e)},50);this.data.core.refreshing=false}},refresh:function(b){var e=this;this.save_opened();b||(b=-1);this.load_node(b,function(){e.__callback({});e.reopen()})},loaded:function(){this.__callback()},set_focus:function(){var b=c.jstree._focused();b&&b!==this&&b.get_container().removeClass("jstree-focused");if(b!==this){this.get_container().addClass("jstree-focused");d=
|
||||||
|
this.get_index()}this.__callback()},is_focused:function(){return d==this.get_index()},_get_node:function(b){var e=c(b,this.get_container());if(e.is(".jstree")||b==-1)return-1;e=e.closest("li",this.get_container());return e.length?e:false},_get_next:function(b,e){b=this._get_node(b);if(b===-1)return this.get_container().find("> ul > li:first-child");if(!b.length)return false;if(e)return b.nextAll("li").size()>0?b.nextAll("li:eq(0)"):false;return b.hasClass("jstree-open")?b.find("li:eq(0)"):b.nextAll("li").size()>
|
||||||
|
0?b.nextAll("li:eq(0)"):b.parentsUntil(this.get_container(),"li").next("li").eq(0)},_get_prev:function(b,e){b=this._get_node(b);if(b===-1)return this.get_container().find("> ul > li:last-child");if(!b.length)return false;if(e)return b.prevAll("li").length>0?b.prevAll("li:eq(0)"):false;if(b.prev("li").length){for(b=b.prev("li").eq(0);b.hasClass("jstree-open");)b=b.children("ul:eq(0)").children("li:last");return b}else{var h=b.parentsUntil(this.get_container(),"li:eq(0)");return h.length?h:false}},
|
||||||
|
_get_parent:function(b){b=this._get_node(b);if(b==-1||!b.length)return false;b=b.parentsUntil(this.get_container(),"li:eq(0)");return b.length?b:-1},_get_children:function(b){b=this._get_node(b);if(b===-1)return this.get_container().children("ul:eq(0)").children("li");if(!b.length)return false;return b.children("ul:eq(0)").children("li")},get_path:function(b,e){var h=[],k=this;b=this._get_node(b);if(b===-1||!b||!b.length)return false;b.parentsUntil(this.get_container(),"li").each(function(){h.push(e?
|
||||||
|
this.id:k.get_text(this))});h.reverse();h.push(e?b.attr("id"):this.get_text(b));return h},open_node:function(b,e,h){b=this._get_node(b);if(!b.length)return false;var k=h?0:this.get_settings().core.animation,j=this;if(this._is_loaded(b)){k&&b.children("ul").css("display","none");b.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading");k&&b.children("ul").slideDown(k,function(){this.style.display=""});this.__callback({obj:b});e&&e.call()}else{b.children("a").addClass("jstree-loading");
|
||||||
|
this.load_node(b,function(){j.open_node(b,e,h)},e)}},close_node:function(b,e){b=this._get_node(b);var h=e?0:this.get_settings().core.animation;if(!b.length)return false;h&&b.children("ul").attr("style","display:block !important");b.removeClass("jstree-open").addClass("jstree-closed");h&&b.children("ul").slideUp(h,function(){this.style.display=""});this.__callback({obj:b})},toggle_node:function(b){b=this._get_node(b);if(b.hasClass("jstree-closed"))return this.open_node(b);if(b.hasClass("jstree-open"))return this.close_node(b)},
|
||||||
|
open_all:function(b,e){b=b?this._get_node(b):this.get_container();if(e)b=b.find("li.jstree-closed");else{e=b;b=b.is(".jstree-closed")?b.find("li.jstree-closed").andSelf():b.find("li.jstree-closed")}var h=this;b.each(function(){var k=this;h._is_loaded(this)?h.open_node(this,false,true):h.open_node(this,function(){h.open_all(k,e)},true)});e.find("li.jstree-closed").length===0&&this.__callback({obj:e})},close_all:function(b){var e=this;b=b?this._get_node(b):this.get_container();b.find("li.jstree-open").andSelf().each(function(){e.close_node(this)});
|
||||||
|
this.__callback({obj:b})},clean_node:function(b){b=b&&b!=-1?c(b):this.get_container();b=b.is("li")?b.find("li").andSelf():b.find("li");b.removeClass("jstree-last").filter("li:last-child").addClass("jstree-last").end().filter(":has(ul)").not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed");b.not(".jstree-open, .jstree-closed").addClass("jstree-leaf");this.__callback({obj:b})},get_rollback:function(){this.__callback();return{i:this.get_index(),h:this.get_container().children("ul").clone(true),
|
||||||
|
d:this.data}},set_rollback:function(b,e){this.get_container().empty().append(b);this.data=e;this.__callback()},load_node:function(b){this.__callback({obj:b})},_is_loaded:function(){return true},create_node:function(b,e,h,k,j){b=this._get_node(b);e=typeof e==="undefined"?"last":e;var i=c("<li>"),m=this.get_settings().core.html_titles,l;if(b!==-1&&!b.length)return false;if(!j&&!this._is_loaded(b)){this.load_node(b,function(){this.create_node(b,e,h,k,true)});return false}this.__rollback();if(typeof h===
|
||||||
|
"string")h={data:h};h||(h={});h.attr&&i.attr(h.attr);h.state&&i.addClass("jstree-"+h.state);if(!h.data)h.data="New node";if(!c.isArray(h.data)){l=h.data;h.data=[];h.data.push(l)}c.each(h.data,function(o,n){l=c("<a>");if(c.isFunction(n))n=n.call(this,h);if(typeof n=="string")l.attr("href","#")[m?"html":"text"](n);else{if(!n.attr)n.attr={};if(!n.attr.href)n.attr.href="#";l.attr(n.attr)[m?"html":"text"](n.title);n.language&&l.addClass(n.language)}l.prepend("<ins class='jstree-icon'> </ins>");if(n.icon)n.icon.indexOf("/")===
|
||||||
|
-1?l.children("ins").addClass(n.icon):l.children("ins").css("background","url('"+n.icon+"') center center no-repeat;");i.append(l)});i.prepend("<ins class='jstree-icon'> </ins>");if(b===-1){b=this.get_container();if(e==="before")e="first";if(e==="after")e="last"}switch(e){case "before":b.before(i);l=this._get_parent(b);break;case "after":b.after(i);l=this._get_parent(b);break;case "inside":case "first":b.children("ul").length||b.append("<ul>");b.children("ul").prepend(i);l=b;break;case "last":b.children("ul").length||
|
||||||
|
b.append("<ul>");b.children("ul").append(i);l=b;break;default:b.children("ul").length||b.append("<ul>");e||(e=0);l=b.children("ul").children("li").eq(e);l.length?l.before(i):b.children("ul").append(i);l=b;break}if(l===-1||l.get(0)===this.get_container().get(0))l=-1;this.clean_node(l);this.__callback({obj:i,parent:l});k&&k.call(this,i);return i},get_text:function(b){b=this._get_node(b);if(!b.length)return false;var e=this.get_settings().core.html_titles;b=b.children("a:eq(0)");if(e){b=b.clone();b.children("INS").remove();
|
||||||
|
return b.html()}else{b=b.contents().filter(function(){return this.nodeType==3})[0];return b.nodeValue}},set_text:function(b,e){b=this._get_node(b);if(!b.length)return false;b=b.children("a:eq(0)");if(this.get_settings().core.html_titles){var h=b.children("INS").clone();b.html(e).prepend(h);this.__callback({obj:b,name:e});return true}else{b=b.contents().filter(function(){return this.nodeType==3})[0];this.__callback({obj:b,name:e});return b.nodeValue=e}},rename_node:function(b,e){b=this._get_node(b);
|
||||||
|
this.__rollback();b&&b.length&&this.set_text.apply(this,Array.prototype.slice.call(arguments))&&this.__callback({obj:b,name:e})},delete_node:function(b){b=this._get_node(b);if(!b.length)return false;this.__rollback();var e=this._get_parent(b);this.deselect_node(b);b=b.remove();e!==-1&&e.find("> ul > li").length===0&&e.removeClass("jstree-open, jstree-closed").addClass("jstree-leaf");this.clean_node(e);this.__callback({obj:b});return b},prepare_move:function(b,e,h,k,j){var i={};i.ot=c.jstree._reference(i.o)||
|
||||||
|
this;i.o=i.ot._get_node(b);i.r=e===-1?-1:this._get_node(e);i.p=typeof i==="undefined"?"last":h;if(!(!j&&f.o&&f.o[0]===i.o[0]&&f.r[0]===i.r[0]&&f.p===i.p)){i.ot=c.jstree._reference(i.o)||this;i.rt=e===-1?i.ot:c.jstree._reference(i.r)||this;if(i.r===-1){i.cr=-1;switch(i.p){case "first":case "before":case "inside":i.cp=0;break;case "after":case "last":i.cp=i.rt.get_container().find(" > ul > li").length;break;default:i.cp=i.p;break}}else{if(!/^(before|after)$/.test(i.p)&&!this._is_loaded(i.r))return this.load_node(i.r,
|
||||||
|
function(){this.prepare_move(b,e,i,k,true)});switch(i.p){case "before":i.cp=i.r.index();i.cr=i.rt._get_parent(i.r);break;case "after":i.cp=i.r.index()+1;i.cr=i.rt._get_parent(i.r);break;case "inside":case "first":i.cp=0;i.cr=i.r;break;case "last":i.cp=i.r.find(" > ul > li").length;i.cr=i.r;break;default:i.cp=i.p;i.cr=i.r;break}}i.np=i.cr==-1?i.rt.get_container():i.cr;i.op=i.ot._get_parent(i.o);i.or=i.np.find(" > ul > li:nth-child("+(i.cp+1)+")");f=i}this.__callback(f);k&&k.call(this,f)},check_move:function(){var b=
|
||||||
|
f;if(b.or[0]===b.o[0]||b.r.parentsUntil(".jstree").andSelf().filter("li").index(b.o)!==-1)return false;return true},move_node:function(b,e,h,k,j,i){if(!j)return this.prepare_move(b,e,h,function(l){this.move_node(l,false,false,k,true,i)});if(!i&&!this.check_move())return false;this.__rollback();e=false;if(k){e=b.o.clone();e.find("*[id]").andSelf().each(function(){if(this.id)this.id="copy_"+this.id})}else e=b.o;if(b.or.length)b.or.before(e);else{b.np.children("ul").length||c("<ul>").appendTo(b.np);
|
||||||
|
b.np.children("ul:eq(0)").append(e)}try{b.ot.clean_node(b.op);b.rt.clean_node(b.np);b.op.find("> ul > li").length||b.op.removeClass("jstree-open jstree-closed").addClass("jstree-leaf").children("ul").remove()}catch(m){}if(k){f.cy=true;f.oc=e}this.__callback(f);return f},_get_move:function(){return f}}})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("ui",{__init:function(){this.data.ui.selected=c();this.data.ui.last_selected=false;this.data.ui.hovered=null;this.data.ui.to_select=this.get_settings().ui.initially_select;this.get_container().delegate("a","click.jstree",c.proxy(function(a){a.preventDefault();this.select_node(a.currentTarget,true,a)},this)).delegate("a","mouseenter.jstree",c.proxy(function(a){this.hover_node(a.target)},this)).delegate("a","mouseleave.jstree",c.proxy(function(a){this.dehover_node(a.target)},
|
||||||
|
this)).bind("reopen.jstree",c.proxy(function(){this.reselect()},this)).bind("get_rollback.jstree",c.proxy(function(){this.dehover_node();this.save_selected()},this)).bind("set_rollback.jstree",c.proxy(function(){this.reselect()},this)).bind("close_node.jstree",c.proxy(function(a,d){var g=this.get_settings().ui,f=this._get_node(d.args[0]),b=f&&f.length?f.find(".jstree-clicked"):[],e=this;g.selected_parent_close===false||!b.length||b.each(function(){e.deselect_node(this);g.selected_parent_close==="select_parent"&&
|
||||||
|
e.select_node(f)})},this)).bind("delete_node.jstree",c.proxy(function(a,d){var g=this._get_node(d.rslt.obj),f=this;(g&&g.length?g.find(".jstree-clicked"):[]).each(function(){f.deselect_node(this)})},this)).bind("move_node.jstree",c.proxy(function(a,d){d.rslt.cy&&d.rslt.oc.find(".jstree-clicked").removeClass("jstree-clicked")},this))},defaults:{select_limit:-1,select_multiple_modifier:"ctrl",selected_parent_close:"select_parent",initially_select:[]},_fn:{_get_node:function(a,d){if(typeof a==="undefined"||
|
||||||
|
a===null)return d?this.data.ui.selected:this.data.ui.last_selected;return this.__call_old()},save_selected:function(){var a=this;this.data.ui.to_select=[];this.data.ui.selected.each(function(){a.data.ui.to_select.push("#"+this.id.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/"))});this.__callback(this.data.ui.to_select)},reselect:function(){var a=this,d=this.data.ui.to_select;d=c.map(c.makeArray(d),function(g){return"#"+g.toString().replace(/^#/,"").replace("\\/","/").replace("/",
|
||||||
|
"\\/")});this.deselect_all();c.each(d,function(g,f){f&&f!=="#"&&a.select_node(f)});this.__callback()},refresh:function(){this.save_selected();return this.__call_old()},hover_node:function(a){a=this._get_node(a);if(!a.length)return false;a.hasClass("jstree-hovered")||this.dehover_node();this.data.ui.hovered=a.children("a").addClass("jstree-hovered").parent();this.__callback({obj:a})},dehover_node:function(){var a=this.data.ui.hovered;if(!a||!a.length)return false;if(this.data.ui.hovered[0]===a.children("a").removeClass("jstree-hovered").parent()[0])this.data.ui.hovered=
|
||||||
|
null;this.__callback({obj:a})},select_node:function(a,d,g){a=this._get_node(a);if(!a.length)return false;var f=this.get_settings().ui;g=f.select_multiple_modifier=="on"||f.select_multiple_modifier!==false&&g&&g[f.select_multiple_modifier+"Key"];var b=this.is_selected(a),e=true;if(d){e=false;switch(true){case b&&!g:break;case !b&&!g:if(f.select_limit==-1||f.select_limit>0){this.deselect_all();e=true}break;case b&&g:this.deselect_node(a);break;case !b&&g:if(f.select_limit==-1||this.data.ui.selected.length+
|
||||||
|
1<=f.select_limit)e=true;break}}if(e&&!b){a.children("a").addClass("jstree-clicked");this.data.ui.selected=this.data.ui.selected.add(a);this.data.ui.last_selected=a;this.__callback({obj:a})}},deselect_node:function(a){a=this._get_node(a);if(!a.length)return false;if(this.is_selected(a)){a.children("a").removeClass("jstree-clicked");this.data.ui.selected=this.data.ui.selected.not(a);if(this.data.ui.last_selected.get(0)===a.get(0))this.data.ui.last_selected=this.data.ui.selected.eq(0);this.__callback({obj:a})}},
|
||||||
|
toggle_select:function(a){a=this._get_node(a);if(!a.length)return false;this.is_selected(a)?this.deselect_node(a):this.select_node(a)},is_selected:function(a){return this.data.ui.selected.index(this._get_node(a))>=0},get_selected:function(a){return a?c(a).find(".jstree-clicked").parent():this.data.ui.selected},deselect_all:function(a){a?c(a).find(".jstree-clicked").removeClass("jstree-clicked"):this.get_container().find(".jstree-clicked").removeClass("jstree-clicked");this.data.ui.selected=c([]);
|
||||||
|
this.data.ui.last_selected=false;this.__callback()}}});c.jstree.defaults.plugins.push("ui")})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("crrm",{__init:function(){this.get_container().bind("move_node.jstree",c.proxy(function(a,d){if(this.get_settings().crrm.move.open_onmove){var g=this;d.rslt.np.parentsUntil(".jstree").andSelf().filter(".jstree-closed").each(function(){g.open_node(this,false,true)})}},this))},defaults:{input_width_limit:200,move:{always_copy:false,open_onmove:true,default_position:"last",check_move:function(){return true}}},_fn:{_show_input:function(a,d){a=this._get_node(a);var g=this.get_settings().crrm.input_width_limit,
|
||||||
|
f=a.children("ins").width(),b=a.find("> a:visible > ins").width()*a.find("> a:visible > ins").length,e=this.get_text(a),h=c("<div>",{css:{position:"absolute",top:"-200px",left:"-1000px",visibility:"hidden"}}).appendTo("body"),k=a.css("position","relative").append(c("<input>",{value:e,css:{padding:"0",border:"1px solid silver",position:"absolute",left:f+b+4+"px",top:"0px",height:this.data.core.li_height-2+"px",lineHeight:this.data.core.li_height-2+"px",width:"150px"},blur:c.proxy(function(){var j=
|
||||||
|
a.children("input"),i=j.val();if(i==="")i=e;this.rename_node(a,i);d.call(this,a,i,e);j.remove();a.css("position","")},this),keyup:function(j){j=j.keyCode||j.which;if(j==27){this.value=e;this.blur()}else j==13?this.blur():k.width(Math.min(h.text("pW"+this.value).width(),g))}})).children("input");this.set_text(a,"");h.css({fontFamily:k.css("fontFamily")||"",fontSize:k.css("fontSize")||"",fontWeight:k.css("fontWeight")||"",fontStyle:k.css("fontStyle")||"",fontStretch:k.css("fontStretch")||"",fontVariant:k.css("fontVariant")||
|
||||||
|
"",letterSpacing:k.css("letterSpacing")||"",wordSpacing:k.css("wordSpacing")||""});k.width(Math.min(h.text("pW"+k[0].value).width(),g))[0].select()},rename:function(a){a=this._get_node(a);this.__rollback();var d=this.__callback;this._show_input(a,function(g,f,b){d.call(this,{obj:g,new_name:f,old_name:b})})},create:function(a,d,g,f,b){var e=this;(a=this._get_node(a))||(a=-1);this.__rollback();return this.create_node(a,d,g,function(h){var k=this._get_parent(h),j=c(h).index();f&&f.call(this,h);k.length&&
|
||||||
|
k.hasClass("jstree-closed")&&this.open_node(k,false,true);b?e.__callback({obj:h,name:this.get_text(h),parent:k,position:j}):this._show_input(h,function(i,m){e.__callback({obj:i,name:m,parent:k,position:j})})})},remove:function(a){a=this._get_node(a,true);this.__rollback();this.delete_node(a);this.__callback({obj:a})},check_move:function(){if(!this.__call_old())return false;if(!this.get_settings().crrm.move.check_move.call(this,this._get_move()))return false;return true},move_node:function(a,d,g,f,
|
||||||
|
b,e){var h=this.get_settings().crrm.move;if(!b){if(!g)g=h.default_position;return this.__call_old(true,a,d,g,f,false,e)}if(h.always_copy===true||h.always_copy==="multitree"&&a.rt.get_index()===a.ot.get_index())f=true;this.__call_old(true,a,d,g,f,true,e)},cut:function(a){a=this._get_node(a);this.data.crrm.cp_nodes=false;this.data.crrm.ct_nodes=false;if(!a||!a.length)return false;this.data.crrm.ct_nodes=a},copy:function(a){a=this._get_node(a);this.data.crrm.cp_nodes=false;this.data.crrm.ct_nodes=false;
|
||||||
|
if(!a||!a.length)return false;this.data.crrm.cp_nodes=a},paste:function(a){a=this._get_node(a);if(!a||!a.length)return false;if(!this.data.crrm.ct_nodes&&!this.data.crrm.cp_nodes)return false;this.data.crrm.ct_nodes&&this.move_node(this.data.crrm.ct_nodes,a);this.data.crrm.cp_nodes&&this.move_node(this.data.crrm.cp_nodes,a,false,true)}}});c.jstree.defaults.plugins.push("crrm")})(jQuery);
|
||||||
|
(function(c){var a=[];c.jstree._themes=false;c.jstree.plugin("themes",{__init:function(){this.get_container().bind("init.jstree",c.proxy(function(){var d=this.get_settings().themes;this.data.themes.dots=d.dots;this.data.themes.icons=d.icons;this.set_theme(d.theme,d.url)},this)).bind("loaded.jstree",c.proxy(function(){this.data.themes.dots?this.show_dots():this.hide_dots();this.data.themes.icons?this.show_icons():this.hide_icons()},this))},defaults:{theme:"default",url:false,dots:true,icons:true},
|
||||||
|
_fn:{set_theme:function(d,g){if(!d)return false;g||(g=c.jstree._themes+d+"/style.css");if(c.inArray(g,a)==-1){c.vakata.css.add_sheet({url:g,rel:"jstree"});a.push(g)}if(this.data.theme!=d){this.get_container().removeClass("jstree-"+this.data.theme);this.data.themes.theme=d}this.get_container().addClass("jstree-"+d);this.data.themes.dots?this.show_dots():this.hide_dots();this.data.themes.icons?this.show_icons():this.hide_icons();this.__callback()},get_theme:function(){return this.data.themes.theme},
|
||||||
|
show_dots:function(){this.data.themes.dots=true;this.get_container().children("ul").removeClass("jstree-no-dots")},hide_dots:function(){this.data.themes.dots=false;this.get_container().children("ul").addClass("jstree-no-dots")},toggle_dots:function(){this.data.themes.dots?this.hide_dots():this.show_dots()},show_icons:function(){this.data.themes.icons=true;this.get_container().children("ul").removeClass("jstree-no-icons")},hide_icons:function(){this.data.themes.icons=false;this.get_container().children("ul").addClass("jstree-no-icons")},
|
||||||
|
toggle_icons:function(){this.data.themes.icons?this.hide_icons():this.show_icons()}}});c(function(){c.jstree._themes===false&&c("script").each(function(){if(this.src.toString().match(/jquery\.jstree[^\/]*?\.js(\?.*)?$/)){c.jstree._themes=this.src.toString().replace(/jquery\.jstree[^\/]*?\.js(\?.*)?$/,"")+"themes/";return false}});if(c.jstree._themes===false)c.jstree._themes="themes/"});c.jstree.defaults.plugins.push("themes")})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("html_data",{__init:function(){this.data.html_data.original_container_html=this.get_container().html().replace(/<\/([^>]+)>\s+</ig,"</$1><").replace(/>\s+<([a-z]{1})/ig,"><$1")},defaults:{data:false,ajax:false,correct_state:false},_fn:{load_node:function(a,d,g){var f=this;this.load_node_html(a,function(){f.__callback({obj:a});d.call(this)},g)},_is_loaded:function(a){a=this._get_node(a);return a==-1||!a||!this.get_settings().html_data.ajax||a.is(".jstree-open, .jstree-leaf")||
|
||||||
|
a.children("ul").children("li").size()>0},load_node_html:function(a,d,g){var f,b=this.get_settings().html_data,e=function(){};switch(true){case !b.data&&!b.ajax:if(!a||a==-1){this.get_container().html(this.data.html_data.original_container_html).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'> </ins>");this.clean_node()}d&&d.call(this);break;case !!b.data&&!b.ajax||!!b.data&&!!b.ajax&&(!a||a===-1):if(!a||a==-1){f=c(b.data);f.is("ul")||
|
||||||
|
(f=c("<ul>").append(f));this.get_container().children("ul").empty().append(f.children()).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'> </ins>");this.clean_node()}d&&d.call(this);break;case !b.data&&!!b.ajax||!!b.data&&!!b.ajax&&a&&a!==-1:a=this._get_node(a);e=function(h,k,j){var i=this.get_settings().html_data.ajax.error;i&&i.call(this,h,k,j);if(a!=-1&&a.length){a.children(".jstree-loading").removeClass("jstree-loading");b.correct_state&&
|
||||||
|
a.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}g&&g.call(this)};b.ajax.context=this;b.ajax.error=e;b.ajax.success=function(h,k,j){if(j.responseText=="")return e.call(this,j,k,"");var i=this.get_settings().html_data.ajax.success;if(i)h=i.call(this,h,k,j)||h;if(h){h=c(h);h.is("ul")||(h=c("<ul>").append(h));if(a==-1||!a)this.get_container().children("ul").empty().append(h.children()).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'> </ins>");
|
||||||
|
else{a.children(".jstree-loading").removeClass("jstree-loading");a.append(h).find("li, a").filter(function(){return this.firstChild.tagName!=="INS"}).prepend("<ins class='jstree-icon'> </ins>")}this.clean_node(a);d&&d.call(this)}else{a.children(".jstree-loading").removeClass("jstree-loading");b.correct_state&&a.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}};if(c.isFunction(b.ajax.data))b.ajax.data=b.ajax.data.call(this,a);c.ajax(b.ajax);break}}}});c.jstree.defaults.plugins.push("html_data")})(jQuery);
|
||||||
|
(function(c){var a=[];c.jstree.plugin("hotkeys",{__init:function(){if(typeof c.hotkeys==="undefined")throw"jsTree hotkeys: jQuery hotkeys plugin not included.";if(!this.data.ui)throw"jsTree hotkeys: jsTree UI plugin not included.";c.each(this.get_settings().hotkeys,function(d){if(c.inArray(d,a)==-1){c(document).bind("keydown",d,function(g){var f;var b=c.jstree._focused();if(b&&b.data&&b.data.hotkeys&&b.data.hotkeys.enabled)if(b.get_settings().hotkeys[d])f=b.get_settings().hotkeys[d].call(b,g);return f});
|
||||||
|
a.push(d)}});this.enable_hotkeys()},defaults:{up:function(){this.hover_node(this._get_prev(this.data.ui.hovered||this.data.ui.last_selected||-1));return false},down:function(){this.hover_node(this._get_next(this.data.ui.hovered||this.data.ui.last_selected||-1));return false},left:function(){var d=this.data.ui.hovered||this.data.ui.last_selected;if(d)d.hasClass("jstree-open")?this.close_node(d):this.hover_node(this._get_prev(d));return false},right:function(){var d=this.data.ui.hovered||this.data.ui.last_selected;
|
||||||
|
if(d&&d.length)d.hasClass("jstree-closed")?this.open_node(d):this.hover_node(this._get_next(d));return false},space:function(){this.data.ui.hovered&&this.data.ui.hovered.children("a:eq(0)").click();return false},"ctrl+space":function(d){d.type="click";this.data.ui.hovered&&this.data.ui.hovered.children("a:eq(0)").trigger(d);return false},f2:function(){this.rename(this.data.ui.hovered||this.data.ui.last_selected)},del:function(){this.remove(this.data.ui.hovered||this._get_node(null))}},_fn:{enable_hotkeys:function(){this.data.hotkeys.enabled=
|
||||||
|
true},disable_hotkeys:function(){this.data.hotkeys.enabled=false}}})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("json_data",{defaults:{data:false,ajax:false,correct_state:false,progressive_render:false},_fn:{load_node:function(a,d,g){var f=this;this.load_node_json(a,function(){f.__callback({obj:a});d.call(this)},g)},_is_loaded:function(a){var d=this.get_settings().json_data;if((a=this._get_node(a))&&a!==-1&&d.progressive_render){a.append(this.parse_json(a.data("jstree-children")));c.removeData(a,"jstree-children");this.clean_node(a)}return a==-1||!a||!d.ajax||a.is(".jstree-open, .jstree-leaf")||
|
||||||
|
a.children("ul").children("li").size()>0},load_node_json:function(a,d,g){var f=this.get_settings().json_data,b=function(){};switch(true){case !f.data&&!f.ajax:throw"Neither data nor ajax settings supplied.";case !!f.data&&!f.ajax||!!f.data&&!!f.ajax&&(!a||a===-1):if(!a||a==-1){this.get_container().children("ul").empty().append(this.parse_json(f.data).children());this.clean_node()}d&&d.call(this);break;case !f.data&&!!f.ajax||!!f.data&&!!f.ajax&&a&&a!==-1:a=this._get_node(a);b=function(e,h,k){var j=
|
||||||
|
this.get_settings().json_data.ajax.error;j&&j.call(this,e,h,k);if(a!=-1&&a.length){a.children(".jstree-loading").removeClass("jstree-loading");f.correct_state&&a.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}g&&g.call(this)};f.ajax.context=this;f.ajax.error=b;f.ajax.success=function(e,h,k){if(k.responseText==""||!c.isArray(e)&&!c.isPlainObject(e))return b.call(this,k,h,"");var j=this.get_settings().json_data.ajax.success;if(j)e=j.call(this,e,h,k)||e;if(e=this.parse_json(e)){a==
|
||||||
|
-1||!a?this.get_container().children("ul").empty().append(e.children()):a.append(e).children(".jstree-loading").removeClass("jstree-loading");this.clean_node(a);d&&d.call(this)}else{a.children(".jstree-loading").removeClass("jstree-loading");f.correct_state&&a.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}};if(c.isFunction(f.ajax.data))f.ajax.data=f.ajax.data.call(this,a);c.ajax(f.ajax);break}},parse_json:function(a,d){var g=c(),f,b,e;b=this.get_settings().json_data;var h=this.get_settings().core.html_titles;
|
||||||
|
if(!a)return g;if(c.isFunction(a))a=a.call(this);if(c.isArray(a)){if(!a.length)return false;b=0;for(e=a.length;b<e;b++){f=this.parse_json(a[b],true);if(f.length)g=g.add(f)}}else{if(typeof a=="string")a={data:a};if(!a.data&&a.data!=="")return g;g=c("<li>");a.attr&&g.attr(a.attr);a.metadata&&g.data("jstree",a.metadata);a.state&&g.addClass("jstree-"+a.state);if(!c.isArray(a.data)){f=a.data;a.data=[];a.data.push(f)}c.each(a.data,function(k,j){f=c("<a>");if(c.isFunction(j))j=j.call(this,a);if(typeof j==
|
||||||
|
"string")f.attr("href","#")[h?"html":"text"](j);else{if(!j.attr)j.attr={};if(!j.attr.href)j.attr.href="#";f.attr(j.attr)[h?"html":"text"](j.title);j.language&&f.addClass(j.language)}f.prepend("<ins class='jstree-icon'> </ins>");if(j.icon)j.icon.indexOf("/")===-1?f.children("ins").addClass(j.icon):f.children("ins").css("background","url('"+j.icon+"') center center no-repeat;");g.append(f)});g.prepend("<ins class='jstree-icon'> </ins>");if(a.children)if(b.progressive_render&&a.state!=="open")g.addClass("jstree-closed").data("jstree-children",
|
||||||
|
a.children);else{if(c.isFunction(a.children))a.children=a.children.call(this,a);if(c.isArray(a.children)&&a.children.length){f=this.parse_json(a.children,true);if(f.length){b=c("<ul>");b.append(f);g.append(b)}}}}if(!d){b=c("<ul>");b.append(g);g=b}return g},get_json:function(a,d,g){var f=[],b=this.get_settings(),e=this,h,k,j,i,m,l;a=this._get_node(a);if(!a||a===-1)a=this.get_container().find("> ul > li");d=c.isArray(d)?d:["id","class"];this.data.types&&d.push(b.types.type_attr);g=c.isArray(g)?g:[];
|
||||||
|
a.each(function(){j=c(this);h={data:[]};if(d.length)h.attr={};c.each(d,function(o,n){if((k=j.attr(n))&&k.length&&k.replace(/jstree[^ ]*|$/ig,"").length)h.attr[n]=k.replace(/jstree[^ ]*|$/ig,"")});if(j.hasClass("jstree-open"))h.state="open";if(j.hasClass("jstree-closed"))h.state="closed";i=j.children("a");i.each(function(){m=c(this);if(g.length||c.inArray("languages",b.plugins)!==-1||m.children("ins").get(0).style.backgroundImage.length||m.children("ins").get(0).className&&m.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,
|
||||||
|
"").length){l=false;c.inArray("languages",b.plugins)!==-1&&c.isArray(b.languages)&&b.languages.length&&c.each(b.languages,function(o,n){if(m.hasClass(n)){l=n;return false}});k={attr:{},title:e.get_text(m,l)};c.each(g,function(o,n){h.attr[n]=j.attr(n).replace(/jstree[^ ]*|$/ig,"")});c.each(b.languages,function(o,n){if(m.hasClass(n)){k.language=n;return true}});if(m.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"").length)k.icon=m.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,
|
||||||
|
"").replace(/^\s+$/ig,"");if(m.children("ins").get(0).style.backgroundImage.length)k.icon=m.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")","")}else k=e.get_text(m);if(i.length>1)h.data.push(k);else h.data=k});j=j.find("> ul > li");if(j.length)h.children=e.get_json(j,d,g);f.push(h)});return f}}})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("languages",{__init:function(){this._load_css()},defaults:[],_fn:{set_lang:function(a){var d=this.get_settings().languages,g=false,f=".jstree-"+this.get_index()+" a";if(!c.isArray(d)||d.length===0)return false;if(c.inArray(a,d)==-1)if(d[a])a=d[a];else return false;if(a==this.data.languages.current_language)return true;g=c.vakata.css.get_css(f+"."+this.data.languages.current_language,false,this.data.languages.language_css);if(g!==false)g.style.display="none";g=c.vakata.css.get_css(f+
|
||||||
|
"."+a,false,this.data.languages.language_css);if(g!==false)g.style.display="";this.data.languages.current_language=a;this.__callback(a);return true},get_lang:function(){return this.data.languages.current_language},get_text:function(a,d){a=this._get_node(a)||this.data.ui.last_selected;if(!a.size())return false;var g=this.get_settings().languages,f=this.get_settings().core.html_titles;if(c.isArray(g)&&g.length){d=d&&c.inArray(d,g)!=-1?d:this.data.languages.current_language;a=a.children("a."+d)}else a=
|
||||||
|
a.children("a:eq(0)");if(f){a=a.clone();a.children("INS").remove();return a.html()}else{a=a.contents().filter(function(){return this.nodeType==3})[0];return a.nodeValue}},set_text:function(a,d,g){a=this._get_node(a)||this.data.ui.last_selected;if(!a.size())return false;var f=this.get_settings().languages,b=this.get_settings().core.html_titles;if(c.isArray(f)&&f.length){g=g&&c.inArray(g,f)!=-1?g:this.data.languages.current_language;a=a.children("a."+g)}else a=a.children("a:eq(0)");if(b){f=a.children("INS").clone();
|
||||||
|
a.html(d).prepend(f);this.__callback({obj:a,name:d,lang:g});return true}else{a=a.contents().filter(function(){return this.nodeType==3})[0];this.__callback({obj:a,name:d,lang:g});return a.nodeValue=d}},_load_css:function(){var a=this.get_settings().languages,d="/* languages css */",g=".jstree-"+this.get_index()+" a",f;if(c.isArray(a)&&a.length){this.data.languages.current_language=a[0];for(f=0;f<a.length;f++){d+=g+"."+a[f]+" {";if(a[f]!=this.data.languages.current_language)d+=" display:none; ";d+=
|
||||||
|
" } "}this.data.languages.language_css=c.vakata.css.add_sheet({str:d})}},create_node:function(a,d,g,f){return this.__call_old(true,a,d,g,function(b){var e=this.get_settings().languages,h=b.children("a"),k;if(c.isArray(e)&&e.length){for(k=0;k<e.length;k++)h.is("."+e[k])||b.append(h.eq(0).clone().removeClass(e.join(" ")).addClass(e[k]));h.not("."+e.join(", .")).remove()}f&&f.call(this,b)})}}})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("cookies",{__init:function(){if(typeof c.cookie==="undefined")throw"jsTree cookie: jQuery cookie plugin not included.";var a=this.get_settings().cookies,d;if(a.save_opened)if((d=c.cookie(a.save_opened))&&d.length)this.data.core.to_open=d.split(",");if(a.save_selected)if((d=c.cookie(a.save_selected))&&d.length)this.data.ui.to_select=d.split(",");this.get_container().one((this.data.ui?"reselect":"reopen")+".jstree",c.proxy(function(){this.get_container().bind("open_node.jstree close_node.jstree select_node.jstree deselect_node.jstree",
|
||||||
|
c.proxy(function(g){this.get_settings().cookies.auto_save&&this.save_cookie((g.handleObj.namespace+g.handleObj.type).replace("jstree",""))},this))},this))},defaults:{save_opened:"jstree_open",save_selected:"jstree_select",auto_save:true,cookie_options:{}},_fn:{save_cookie:function(a){if(!this.data.core.refreshing){var d=this.get_settings().cookies;if(a)switch(a){case "open_node":case "close_node":if(d.save_opened){this.save_opened();c.cookie(d.save_opened,this.data.core.to_open.join(","),d.cookie_options)}break;
|
||||||
|
case "select_node":case "deselect_node":if(d.save_selected&&this.data.ui){this.save_selected();c.cookie(d.save_selected,this.data.ui.to_select.join(","),d.cookie_options)}break}else{if(d.save_opened){this.save_opened();c.cookie(d.save_opened,this.data.core.to_open.join(","),d.cookie_options)}if(d.save_selected&&this.data.ui){this.save_selected();c.cookie(d.save_selected,this.data.ui.to_select.join(","),d.cookie_options)}}}}}});c.jstree.defaults.plugins.push("cookies")})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("sort",{__init:function(){this.get_container().bind("load_node.jstree",c.proxy(function(a,d){var g=this._get_node(d.rslt.obj);g=g===-1?this.get_container().children("ul"):g.children("ul");this.sort(g)},this)).bind("rename_node.jstree",c.proxy(function(a,d){this.sort(d.rslt.obj.parent())},this)).bind("move_node.jstree",c.proxy(function(a,d){this.sort((d.rslt.np==-1?this.get_container():d.rslt.np).children("ul"))},this))},defaults:function(a,d){return this.get_text(a)>this.get_text(d)?
|
||||||
|
1:-1},_fn:{sort:function(a){var d=this.get_settings().sort,g=this;a.append(c.makeArray(a.children("li")).sort(c.proxy(d,g)));a.find("> li > ul").each(function(){g.sort(c(this))});this.clean_node(a)}}})})(jQuery);
|
||||||
|
(function(c){var a=false,d=false,g=false;c.vakata.dnd={is_down:false,is_drag:false,helper:false,init_x:0,init_y:0,threshold:5,user_data:{},drag_start:function(f,b,e){c.vakata.dnd.is_drag&&c.vakata.drag_stop({});try{f.currentTarget.unselectable="on";f.currentTarget.onselectstart=function(){return false};if(f.currentTarget.style)f.currentTarget.style.MozUserSelect="none"}catch(h){}c.vakata.dnd.init_x=f.pageX;c.vakata.dnd.init_y=f.pageY;c.vakata.dnd.user_data=b;c.vakata.dnd.is_down=true;c.vakata.dnd.helper=
|
||||||
|
c("<div id='vakata-dragged'>").html(e).css("opacity","0.75");c(document).bind("mousemove",c.vakata.dnd.drag);c(document).bind("mouseup",c.vakata.dnd.drag_stop);return false},drag:function(f){if(c.vakata.dnd.is_down){if(!c.vakata.dnd.is_drag)if(Math.abs(f.pageX-c.vakata.dnd.init_x)>5||Math.abs(f.pageY-c.vakata.dnd.init_y)>5){c.vakata.dnd.helper.appendTo("body");c.vakata.dnd.is_drag=true;c(document).triggerHandler("vakata.drag_start",{event:f,data:c.vakata.dnd.user_data})}else return;c.vakata.dnd.helper.css({left:f.pageX+
|
||||||
|
5+"px",top:f.pageY+10+"px"});c(document).triggerHandler("vakata.drag",{event:f,data:c.vakata.dnd.user_data})}},drag_stop:function(f){c(document).unbind("mousemove",c.vakata.dnd.drag);c(document).unbind("mouseup",c.vakata.dnd.drag_stop);c(document).triggerHandler("vakata.drag_stop",{event:f,data:c.vakata.dnd.user_data});c.vakata.dnd.helper.remove();c.vakata.dnd.init_x=0;c.vakata.dnd.init_y=0;c.vakata.dnd.user_data={};c.vakata.dnd.is_down=false;c.vakata.dnd.is_drag=false}};c(function(){c.vakata.css.add_sheet({str:"#vakata-dragged { display:block; margin:0 0 0 0; padding:4px 4px 4px 24px; position:absolute; left:-2000px; top:-2000px; line-height:16px; } "})});
|
||||||
|
c.jstree.plugin("dnd",{__init:function(){this.data.dnd={active:false,after:false,inside:false,before:false,off:false,prepared:false,w:0,to1:false,to2:false,cof:false,cw:false,ch:false,i1:false,i2:false};this.get_container().bind("mouseenter.jstree",c.proxy(function(){if(c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree&&this.data.themes){g.attr("class","jstree-"+this.data.themes.theme);c.vakata.dnd.helper.attr("class","jstree-dnd-helper jstree-"+this.data.themes.theme)}},this)).bind("mouseleave.jstree",
|
||||||
|
c.proxy(function(){if(c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree){this.data.dnd.i1&&clearInterval(this.data.dnd.i1);this.data.dnd.i2&&clearInterval(this.data.dnd.i2)}},this)).bind("mousemove.jstree",c.proxy(function(b){if(c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree){var e=this.get_container()[0];if(b.pageX+20>this.data.dnd.cof.left+this.data.dnd.cw){this.data.dnd.i1&&clearInterval(this.data.dnd.i1);this.data.dnd.i1=setInterval(c.proxy(function(){this.scrollLeft+=5},e),100)}else if(b.pageX-
|
||||||
|
20<this.data.dnd.cof.left){this.data.dnd.i1&&clearInterval(this.data.dnd.i1);this.data.dnd.i1=setInterval(c.proxy(function(){this.scrollLeft-=5},e),100)}else this.data.dnd.i1&&clearInterval(this.data.dnd.i1);if(b.pageY+20>this.data.dnd.cof.top+this.data.dnd.ch){this.data.dnd.i2&&clearInterval(this.data.dnd.i2);this.data.dnd.i2=setInterval(c.proxy(function(){this.scrollTop+=5},e),100)}else if(b.pageY-20<this.data.dnd.cof.top){this.data.dnd.i2&&clearInterval(this.data.dnd.i2);this.data.dnd.i2=setInterval(c.proxy(function(){this.scrollTop-=
|
||||||
|
5},e),100)}else this.data.dnd.i2&&clearInterval(this.data.dnd.i2)}},this)).delegate("a","mousedown.jstree",c.proxy(function(b){this.start_drag(b.currentTarget,b);return false},this)).delegate("a","mouseenter.jstree",c.proxy(function(b){c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree&&this.dnd_enter(b.currentTarget)},this)).delegate("a","mousemove.jstree",c.proxy(function(b){if(c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree){if(typeof this.data.dnd.off.top==="undefined")this.data.dnd.off=
|
||||||
|
c(b.target).offset();this.data.dnd.w=(b.pageY-(this.data.dnd.off.top||0))%this.data.core.li_height;if(this.data.dnd.w<0)this.data.dnd.w+=this.data.core.li_height;this.dnd_show()}},this)).delegate("a","mouseleave.jstree",c.proxy(function(b){if(c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree){this.data.dnd.after=false;this.data.dnd.before=false;this.data.dnd.inside=false;c.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");g.hide();if(d&&d[0]===b.target.parentNode){if(this.data.dnd.to1){clearTimeout(this.data.dnd.to1);
|
||||||
|
this.data.dnd.to1=false}if(this.data.dnd.to2){clearTimeout(this.data.dnd.to2);this.data.dnd.to2=false}}}},this)).delegate("a","mouseup.jstree",c.proxy(function(b){c.vakata.dnd.is_drag&&c.vakata.dnd.user_data.jstree&&this.dnd_finish(b)},this));c(document).bind("vakata.drag_stop",c.proxy(function(){this.data.dnd.after=false;this.data.dnd.before=false;this.data.dnd.inside=false;this.data.dnd.off=false;this.data.dnd.prepared=false;this.data.dnd.w=false;this.data.dnd.to1=false;this.data.dnd.to2=false;
|
||||||
|
this.data.dnd.active=false;this.data.dnd.foreign=false;g&&g.css({left:"-2000px",top:"-2000px"})},this)).bind("vakata.drag_start",c.proxy(function(b,e){if(e.data.jstree){var h=c(e.event.target);h.closest(".jstree").hasClass("jstree-"+this.get_index())&&this.dnd_enter(h)}},this));var f=this.get_settings().dnd;f.drag_target&&c(document).delegate(f.drag_target,"mousedown.jstree",c.proxy(function(b){a=b.target;c.vakata.dnd.drag_start(b,{jstree:true,obj:b.target},"<ins class='jstree-icon'></ins>"+c(b.target).text());
|
||||||
|
if(this.data.themes){g.attr("class","jstree-"+this.data.themes.theme);c.vakata.dnd.helper.attr("class","jstree-dnd-helper jstree-"+this.data.themes.theme)}c.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");b=this.get_container();this.data.dnd.cof=b.children("ul").offset();this.data.dnd.cw=parseInt(b.width(),10);this.data.dnd.ch=parseInt(b.height(),10);this.data.dnd.foreign=true;return false},this));f.drop_target&&c(document).delegate(f.drop_target,"mouseenter.jstree",c.proxy(function(b){this.data.dnd.active&&
|
||||||
|
this.get_settings().dnd.drop_check.call(this,{o:a,r:c(b.target)})&&c.vakata.dnd.helper.children("ins").attr("class","jstree-ok")},this)).delegate(f.drop_target,"mouseleave.jstree",c.proxy(function(){this.data.dnd.active&&c.vakata.dnd.helper.children("ins").attr("class","jstree-invalid")},this)).delegate(f.drop_target,"mouseup.jstree",c.proxy(function(b){this.data.dnd.active&&c.vakata.dnd.helper.children("ins").hasClass("jstree-ok")&&this.get_settings().dnd.drop_finish.call(this,{o:a,r:c(b.target)})},
|
||||||
|
this))},defaults:{copy_modifier:"ctrl",check_timeout:200,open_timeout:500,drop_target:".jstree-drop",drop_check:function(){return true},drop_finish:c.noop,drag_target:".jstree-draggable",drag_finish:c.noop,drag_check:function(){return{after:false,before:false,inside:true}}},_fn:{dnd_prepare:function(){this.data.dnd.off=d.offset();if(this.data.dnd.foreign){var f=this.get_settings().dnd.drag_check.call(this,{o:a,r:d});this.data.dnd.after=f.after;this.data.dnd.before=f.before;this.data.dnd.inside=f.inside;
|
||||||
|
this.data.dnd.prepared=true;return this.dnd_show()}this.prepare_move(a,d,"before");this.data.dnd.before=this.check_move();this.prepare_move(a,d,"after");this.data.dnd.after=this.check_move();if(this._is_loaded(d)){this.prepare_move(a,d,"inside");this.data.dnd.inside=this.check_move()}else this.data.dnd.inside=false;this.data.dnd.prepared=true;return this.dnd_show()},dnd_show:function(){if(this.data.dnd.prepared){var f=["before","inside","after"],b=false;f=this.data.dnd.w<this.data.core.li_height/
|
||||||
|
3?["before","inside","after"]:this.data.dnd.w<=this.data.core.li_height*2/3?this.data.dnd.w<this.data.core.li_height/2?["inside","before","after"]:["inside","after","before"]:["after","inside","before"];c.each(f,c.proxy(function(e,h){if(this.data.dnd[h]){c.vakata.dnd.helper.children("ins").attr("class","jstree-ok");b=h;return false}},this));b===false&&c.vakata.dnd.helper.children("ins").attr("class","jstree-invalid");switch(b){case "before":g.css({left:this.data.dnd.off.left+10+"px",top:this.data.dnd.off.top-
|
||||||
|
6+"px"}).show();break;case "after":g.css({left:this.data.dnd.off.left+10+"px",top:this.data.dnd.off.top+this.data.core.li_height-7+"px"}).show();break;case "inside":g.css({left:this.data.dnd.off.left+14+"px",top:this.data.dnd.off.top+this.data.core.li_height/2-5+"px"}).show();break;default:g.hide();break}return b}},dnd_open:function(){this.data.dnd.to2=false;this.open_node(d,c.proxy(this.dnd_prepare,this),true)},dnd_finish:function(f){if(this.data.dnd.foreign){if(this.data.dnd.after||this.data.dnd.before||
|
||||||
|
this.data.dnd.inside)this.get_settings().dnd.drag_finish.call(this,{o:a,r:d})}else{this.dnd_prepare();this.move_node(a,d,this.dnd_show(),f[this.get_settings().dnd.copy_modifier+"Key"])}d=a=false;g.hide()},dnd_enter:function(f){var b=this.get_settings().dnd;this.data.dnd.prepared=false;d=this._get_node(f);if(b.check_timeout){this.data.dnd.to1&&clearTimeout(this.data.dnd.to1);this.data.dnd.to1=setTimeout(c.proxy(this.dnd_prepare,this),b.check_timeout)}else this.dnd_prepare();if(b.open_timeout){this.data.dnd.to2&&
|
||||||
|
clearTimeout(this.data.dnd.to2);if(d.hasClass("jstree-closed"))this.data.dnd.to2=setTimeout(c.proxy(this.dnd_open,this),b.open_timeout)}else d.hasClass("jstree-closed")&&this.dnd_open()},start_drag:function(f,b){a=this._get_node(f);if(this.data.ui&&this.is_selected(a))a=this._get_node(null,true);c.vakata.dnd.drag_start(b,{jstree:true,obj:a},"<ins class='jstree-icon'></ins>"+(a.length>1?"Multiple selection":this.get_text(a)));if(this.data.themes){g.attr("class","jstree-"+this.data.themes.theme);c.vakata.dnd.helper.attr("class",
|
||||||
|
"jstree-dnd-helper jstree-"+this.data.themes.theme)}var e=this.get_container();this.data.dnd.cof=e.children("ul").offset();this.data.dnd.cw=parseInt(e.width(),10);this.data.dnd.ch=parseInt(e.height(),10);this.data.dnd.active=true}}});c(function(){c.vakata.css.add_sheet({str:"#vakata-dragged ins { display:block; text-decoration:none; width:16px; height:16px; margin:0 0 0 0; padding:0; position:absolute; top:4px; left:4px; } #vakata-dragged .jstree-ok { background:green; } #vakata-dragged .jstree-invalid { background:red; } #jstree-marker { padding:0; margin:0; line-height:12px; font-size:1px; overflow:hidden; height:12px; width:8px; position:absolute; left:-45px; top:-30px; z-index:1000; background-repeat:no-repeat; display:none; background-color:silver; } "});
|
||||||
|
g=c("<div>").attr({id:"jstree-marker"}).hide().appendTo("body");c(document).bind("vakata.drag_start",function(f,b){b.data.jstree&&g.show()});c(document).bind("vakata.drag_stop",function(f,b){b.data.jstree&&g.hide()})})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("checkbox",{__init:function(){if(!this.data.ui)throw"jsTree checkboxes: jsTree UI plugin not included";this.select_node=this.deselect_node=this.deselect_all=c.noop;this.get_selected=this.get_checked;this.get_container().bind("open_node.jstree create_node.jstree",c.proxy(function(a,d){this._prepare_checkboxes(d.rslt.obj)},this)).bind("loaded.jstree",c.proxy(function(){this._prepare_checkboxes()},this)).bind("clean_node.jstree",c.proxy(function(a,d){this._repair_state(d.args[0])},
|
||||||
|
this)).delegate("a","click.jstree",c.proxy(function(a){this.change_state(a.target);this.save_selected();this.data.cookies&&this.save_cookie("select_node");a.preventDefault()},this))},_fn:{_prepare_checkboxes:function(a){a=!a||a==-1?this.get_container():this._get_node(a);var d=a.is("li")&&a.hasClass("jstree-checked")?"jstree-checked":"jstree-unchecked";a.find("a").not(":has(.checkbox)").prepend("<ins class='checkbox'> </ins>").parent().addClass(d)},change_state:function(a,d){a=this._get_node(a);
|
||||||
|
if(d=d===false||d===true?d:a.hasClass("jstree-checked"))a.find("li").andSelf().removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked");else{a.find("li").andSelf().removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked");this.data.ui.last_selected=a}var g=this;a.parentsUntil(this.get_container(),"li").each(function(){var f=c(this);if(d)if(f.children("ul").children(".jstree-checked, .jstree-undetermined").length){f.parentsUntil(g.get_container(),"li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");
|
||||||
|
return false}else f.removeClass("jstree-checked jstree-undetermined").addClass("jstree-unchecked");else if(f.children("ul").children(".jstree-unchecked, .jstree-undetermined").length){f.parentsUntil(g.get_container(),"li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined");return false}else f.removeClass("jstree-unchecked jstree-undetermined").addClass("jstree-checked")});this.data.ui.selected=this.get_checked();this.__callback(a)},check_node:function(a){this.change_state(a,
|
||||||
|
false)},uncheck_node:function(a){this.change_state(a,true)},check_all:function(){var a=this;this.get_container().children("ul").children("li").each(function(){a.check_node(this,false)})},uncheck_all:function(){var a=this;this.get_container().children("ul").children("li").each(function(){a.change_state(this,true)})},is_checked:function(a){a=this._get_node(a);return a.length?a.is(".jstree-checked"):false},get_checked:function(a){a=!a||a===-1?this.get_container():this._get_node(a);return a.find("> ul > .jstree-checked, .jstree-undetermined > ul > .jstree-checked")},
|
||||||
|
get_unchecked:function(a){a=!a||a===-1?this.get_container():this._get_node(a);return a.find("> ul > .jstree-unchecked, .jstree-undetermined > ul > .jstree-unchecked")},show_checkboxes:function(){this.get_container().children("ul").removeClass("jstree-no-checkboxes")},hide_checkboxes:function(){this.get_container().children("ul").addClass("jstree-no-checkboxes")},_repair_state:function(a){a=this._get_node(a);if(a.length){var d=a.find("> ul > .jstree-checked").length,g=a.find("> ul > .jstree-undetermined").length,
|
||||||
|
f=a.find("> ul > li").length;if(f===0)a.hasClass("jstree-undetermined")&&this.check_node(a);else if(d===0&&g===0)this.uncheck_node(a);else d===f?this.check_node(a):a.parentsUntil(this.get_container(),"li").andSelf().removeClass("jstree-checked jstree-unchecked").addClass("jstree-undetermined")}},reselect:function(){var a=this,d=this.data.ui.to_select;d=c.map(c.makeArray(d),function(g){return"#"+g.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/")});this.deselect_all();c.each(d,function(g,
|
||||||
|
f){a.check_node(f)});this.__callback()}}})})(jQuery);
|
||||||
|
(function(c){c.vakata.xslt=function(d,g){var f="",b,e;if(document.recalc){b=document.createElement("xml");e=document.createElement("xml");b.innerHTML=d;e.innerHTML=g;c("body").append(b).append(e);f=b.transformNode(e.XMLDocument);c("body").remove(b).remove(e);return f}if(typeof window.DOMParser!=="undefined"&&typeof window.XMLHttpRequest!=="undefined"&&typeof window.XSLTProcessor!=="undefined"){b=new XSLTProcessor;f=c.isFunction(b.transformDocument)?typeof window.XMLSerializer!=="undefined":true;if(!f)return false;
|
||||||
|
d=(new DOMParser).parseFromString(d,"text/xml");g=(new DOMParser).parseFromString(g,"text/xml");if(c.isFunction(b.transformDocument)){f=document.implementation.createDocument("","",null);b.transformDocument(d,g,f,null);return(new XMLSerializer).serializeToString(f)}else{b.importStylesheet(g);f=b.transformToFragment(d,document);return c("<div>").append(f).html()}}return false};var a={nest:'<?xml version="1.0" encoding="utf-8" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ><xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/html" /><xsl:template match="/">\t<xsl:call-template name="nodes">\t\t<xsl:with-param name="node" select="/root" />\t</xsl:call-template></xsl:template><xsl:template name="nodes">\t<xsl:param name="node" />\t<ul>\t<xsl:for-each select="$node/item">\t\t<xsl:variable name="children" select="count(./item) > 0" />\t\t<li>\t\t\t<xsl:attribute name="class">\t\t\t\t<xsl:if test="position() = last()">jstree-last </xsl:if>\t\t\t\t<xsl:choose>\t\t\t\t\t<xsl:when test="@state = \'open\'">jstree-open </xsl:when>\t\t\t\t\t<xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>\t\t\t\t\t<xsl:otherwise>jstree-leaf </xsl:otherwise>\t\t\t\t</xsl:choose>\t\t\t\t<xsl:value-of select="@class" />\t\t\t</xsl:attribute>\t\t\t<xsl:for-each select="@*">\t\t\t\t<xsl:if test="name() != \'class\' and name() != \'state\' and name() != \'hasChildren\'">\t\t\t\t\t<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>\t\t\t\t</xsl:if>\t\t\t</xsl:for-each>\t<ins class="jstree-icon"><xsl:text> </xsl:text></ins>\t\t\t<xsl:for-each select="content/name">\t\t\t\t<a>\t\t\t\t<xsl:attribute name="href">\t\t\t\t\t<xsl:choose>\t\t\t\t\t<xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>\t\t\t\t\t<xsl:otherwise>#</xsl:otherwise>\t\t\t\t\t</xsl:choose>\t\t\t\t</xsl:attribute>\t\t\t\t<xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>\t\t\t\t<xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute>\t\t\t\t<xsl:for-each select="@*">\t\t\t\t\t<xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">\t\t\t\t\t\t<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>\t\t\t\t\t</xsl:if>\t\t\t\t</xsl:for-each>\t\t\t\t\t<ins>\t\t\t\t\t\t<xsl:attribute name="class">jstree-icon \t\t\t\t\t\t\t<xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>\t\t\t\t\t\t</xsl:attribute>\t\t\t\t\t\t<xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>\t\t\t\t\t\t<xsl:text> </xsl:text>\t\t\t\t\t</ins>\t\t\t\t\t<xsl:value-of select="current()" />\t\t\t\t</a>\t\t\t</xsl:for-each>\t\t\t<xsl:if test="$children or @hasChildren"><xsl:call-template name="nodes"><xsl:with-param name="node" select="current()" /></xsl:call-template></xsl:if>\t\t</li>\t</xsl:for-each>\t</ul></xsl:template></xsl:stylesheet>',
|
||||||
|
flat:'<?xml version="1.0" encoding="utf-8" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ><xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" standalone="no" indent="no" media-type="text/xml" /><xsl:template match="/">\t<ul>\t<xsl:for-each select="//item[not(@parent_id) or @parent_id=0]">\t\t<xsl:call-template name="nodes">\t\t\t<xsl:with-param name="node" select="." />\t\t\t<xsl:with-param name="is_last" select="number(position() = last())" />\t\t</xsl:call-template>\t</xsl:for-each>\t</ul></xsl:template><xsl:template name="nodes">\t<xsl:param name="node" />\t<xsl:param name="is_last" />\t<xsl:variable name="children" select="count(//item[@parent_id=$node/attribute::id]) > 0" />\t<li>\t<xsl:attribute name="class">\t\t<xsl:if test="$is_last = true()">jstree-last </xsl:if>\t\t<xsl:choose>\t\t\t<xsl:when test="@state = \'open\'">jstree-open </xsl:when>\t\t\t<xsl:when test="$children or @hasChildren or @state = \'closed\'">jstree-closed </xsl:when>\t\t\t<xsl:otherwise>jstree-leaf </xsl:otherwise>\t\t</xsl:choose>\t\t<xsl:value-of select="@class" />\t</xsl:attribute>\t<xsl:for-each select="@*">\t\t<xsl:if test="name() != \'parent_id\' and name() != \'hasChildren\' and name() != \'class\' and name() != \'state\'">\t\t<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>\t\t</xsl:if>\t</xsl:for-each>\t<ins class="jstree-icon"><xsl:text> </xsl:text></ins>\t<xsl:for-each select="content/name">\t\t<a>\t\t<xsl:attribute name="href">\t\t\t<xsl:choose>\t\t\t<xsl:when test="@href"><xsl:value-of select="@href" /></xsl:when>\t\t\t<xsl:otherwise>#</xsl:otherwise>\t\t\t</xsl:choose>\t\t</xsl:attribute>\t\t<xsl:attribute name="class"><xsl:value-of select="@lang" /> <xsl:value-of select="@class" /></xsl:attribute>\t\t<xsl:attribute name="style"><xsl:value-of select="@style" /></xsl:attribute>\t\t<xsl:for-each select="@*">\t\t\t<xsl:if test="name() != \'style\' and name() != \'class\' and name() != \'href\'">\t\t\t\t<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>\t\t\t</xsl:if>\t\t</xsl:for-each>\t\t\t<ins>\t\t\t\t<xsl:attribute name="class">jstree-icon \t\t\t\t\t<xsl:if test="string-length(attribute::icon) > 0 and not(contains(@icon,\'/\'))"><xsl:value-of select="@icon" /></xsl:if>\t\t\t\t</xsl:attribute>\t\t\t\t<xsl:if test="string-length(attribute::icon) > 0 and contains(@icon,\'/\')"><xsl:attribute name="style">background:url(<xsl:value-of select="@icon" />) center center no-repeat;</xsl:attribute></xsl:if>\t\t\t\t<xsl:text> </xsl:text>\t\t\t</ins>\t\t\t<xsl:value-of select="current()" />\t\t</a>\t</xsl:for-each>\t<xsl:if test="$children">\t\t<ul>\t\t<xsl:for-each select="//item[@parent_id=$node/attribute::id]">\t\t\t<xsl:call-template name="nodes">\t\t\t\t<xsl:with-param name="node" select="." />\t\t\t\t<xsl:with-param name="is_last" select="number(position() = last())" />\t\t\t</xsl:call-template>\t\t</xsl:for-each>\t\t</ul>\t</xsl:if>\t</li></xsl:template></xsl:stylesheet>'};
|
||||||
|
c.jstree.plugin("xml_data",{defaults:{data:false,ajax:false,xsl:"flat",clean_node:false},_fn:{load_node:function(d,g,f){var b=this;this.load_node_xml(d,function(){b.__callback({obj:d});g.call(this)},f)},_is_loaded:function(d){var g=this.get_settings().xml_data;return d==-1||!d||!g.ajax||d.is(".jstree-open, .jstree-leaf")||d.children("ul").children("li").size()>0},load_node_xml:function(d,g,f){var b=this.get_settings().xml_data,e=function(){};switch(true){case !b.data&&!b.ajax:throw"Neither data nor ajax settings supplied.";
|
||||||
|
case !!b.data&&!b.ajax||!!b.data&&!!b.ajax&&(!d||d===-1):if(!d||d==-1){this.get_container().children("ul").empty().append(this.parse_xml(b.data).children());b.clean_node&&this.clean_node(d)}g&&g.call(this);break;case !b.data&&!!b.ajax||!!b.data&&!!b.ajax&&d&&d!==-1:d=this._get_node(d);e=function(h,k,j){var i=this.get_settings().xml_data.ajax.error;i&&i.call(this,h,k,j);if(d!==-1&&d.length){d.children(".jstree-loading").removeClass("jstree-loading");b.correct_state&&d.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}f&&
|
||||||
|
f.call(this)};b.ajax.context=this;b.ajax.error=e;b.ajax.success=function(h,k,j){if(j.responseText=="")return e.call(this,j,k,"");h=j.responseText;var i=this.get_settings().xml_data.ajax.success;if(i)h=i.call(this,h,k,j)||h;if(h=this.parse_xml(h)){d===-1||!d?this.get_container().children("ul").empty().append(this.parse_xml(j.responseText).children()):d.append(this.parse_xml(j.responseText)).children(".jstree-loading").removeClass("jstree-loading");b.clean_node&&this.clean_node(d);g&&g.call(this)}else{d.children(".jstree-loading").removeClass("jstree-loading");
|
||||||
|
b.correct_state&&d.removeClass("jstree-open jstree-closed").addClass("jstree-leaf")}};if(c.isFunction(b.ajax.data))b.ajax.data=b.ajax.data.call(null,d);c.ajax(b.ajax);break}},parse_xml:function(d){var g=this.get_settings().xml_data;d=c.vakata.xslt(d,a[g.xsl]);if(d!==false)d=c(d);return d},get_xml:function(d,g,f,b,e){var h="",k=this.get_settings(),j=this,i,m,l,o,n;d||(d="flat");e||(e=0);g=this._get_node(g);if(!g||g===-1)g=this.get_container().find("> ul > li");f=c.isArray(f)?f:["id","class"];this.data.types&&
|
||||||
|
f.push(k.types.type_attr);b=c.isArray(b)?b:[];e||(h+="<root>");g.each(function(){h+="<item";l=c(this);c.each(f,function(q,p){h+=" "+p+'="'+l.attr(p).replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"")+'"'});if(l.hasClass("jstree-open"))h+=' state="open"';if(l.hasClass("jstree-closed"))h+=' state="closed"';if(d==="flat")h+=' parent_id="'+e+'"';h+=">";h+="<content>";o=l.children("a");o.each(function(){i=c(this);n=false;h+="<name";c.inArray("languages",k.plugins)!==-1&&c.each(k.languages,function(q,
|
||||||
|
p){if(i.hasClass(p)){h+=' lang="'+p+'"';n=p;return false}});b.length&&c.each(b,function(q,p){h+=" "+p+'="'+l.attr(p).replace(/jstree[^ ]*|$/ig,"")+'"'});if(i.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"").length)h+=' icon="'+i.children("ins").get(0).className.replace(/jstree[^ ]*|$/ig,"").replace(/^\s+$/ig,"")+'"';if(i.children("ins").get(0).style.backgroundImage.length)h+=' icon="'+i.children("ins").get(0).style.backgroundImage.replace("url(","").replace(")",
|
||||||
|
"")+'"';h+=">";h+="<![CDATA["+j.get_text(i,n)+"]]\>";h+="</name>"});h+="</content>";m=l[0].id;l=l.find("> ul > li");if(l.length)m=j.get_xml(d,l,f,b,m);if(d=="nest")h+=m;h+="</item>";if(d=="flat")h+=m});e||(h+="</root>");return h}}})})(jQuery);
|
||||||
|
(function(c){c.expr[":"].jstree_contains=function(a,d,g){return(a.textContent||a.innerText||"").toLowerCase().indexOf(g[3].toLowerCase())>=0};c.jstree.plugin("search",{__init:function(){this.data.search.str="";this.data.search.result=c()},defaults:{ajax:false,case_insensitive:false},_fn:{search:function(a,d){var g=this.get_settings().search,f=this;this.data.search.str=a;if(!d&&g.ajax!==false&&this.get_container().find(".jstree-closed:eq(0)").length>0){this.search.supress_callback=true;g.ajax.context=
|
||||||
|
this;g.ajax.error=function(){};g.ajax.success=function(b,e,h){var k=this.get_settings().search.ajax.success;if(k)b=k.call(this,b,e,h)||b;this.data.search.to_open=b;this._search_open()};if(c.isFunction(g.ajax.data))g.ajax.data=g.ajax.data.call(this,a);if(!g.ajax.data)g.ajax.data={search_string:a};if(!g.ajax.dataType||/^json/.exec(g.ajax.dataType))g.ajax.dataType="json";c.ajax(g.ajax)}else{this.data.search.result.length&&this.clear_search();this.data.search.result=this.get_container().find("a"+(this.data.languages?
|
||||||
|
"."+this.get_lang():"")+":"+(g.case_insensitive?"jstree_contains":"contains")+"("+this.data.search.str+")");this.data.search.result.addClass("jstree-search").parents(".jstree-closed").each(function(){f.open_node(this,false,true)});this.__callback({nodes:this.data.search.result,str:a})}},clear_search:function(){this.data.search.result.removeClass("jstree-search");this.__callback(this.data.search.result);this.data.search.result=c()},_search_open:function(){var a=this,d=true,g=[],f=[];if(this.data.search.to_open.length){c.each(this.data.search.to_open,
|
||||||
|
function(b,e){if(e=="#")return true;c(e).length&&c(e).is(".jstree-closed")?g.push(e):f.push(e)});if(g.length){this.data.search.to_open=f;c.each(g,function(b,e){a.open_node(e,function(){a._search_open(true)})});d=false}}d&&this.search(this.data.search.str,true)}}})})(jQuery);
|
||||||
|
(function(c){c.vakata.context={cnt:c("<div id='vakata-contextmenu'>"),vis:false,tgt:false,func:false,data:false,show:function(a,d,g,f,b){if(a=c.vakata.context.parse(a)){c.vakata.context.vis=true;c.vakata.context.tgt=d;c.vakata.context.data=b||null;c.vakata.context.cnt.html(a).css({visibility:"hidden",display:"block",left:0,top:0});b=c.vakata.context.cnt.height();a=c.vakata.context.cnt.width();if(g+a>c(document).width()){g=c(document).width()-(a+5);c.vakata.context.cnt.find("li > ul").addClass("right")}if(f+
|
||||||
|
b>c(document).height()){f-=b+d[0].offsetHeight;c.vakata.context.cnt.find("li > ul").addClass("bottom")}c.vakata.context.cnt.css({left:g,top:f}).find("li:has(ul)").bind("mouseenter",function(){var e=c(document).width(),h=c(document).height(),k=c(this).children("ul").show();e!==c(document).width()&&k.toggleClass("right");h!==c(document).height()&&k.toggleClass("bottom")}).bind("mouseleave",function(){c(this).children("ul").hide()}).end().css({visibility:"visible"}).show();c(document).triggerHandler("vakata.context_show")}},
|
||||||
|
hide:function(){c.vakata.context.vis=false;c.vakata.context.cnt.attr("class","").hide();c(document).triggerHandler("vakata.context_hide")},parse:function(a,d){var g="",f=false;if(!d)c.vakata.context.func={};g+="<ul>";c.each(a,function(b,e){if(!e)return true;c.vakata.context.func[b]=e.action;if(e.separator_before)g+="<li class='vakata-separator vakata-separator-before'></li>";g+="<li><ins ";if(e.icon&&e.icon.indexOf("/")===-1)g+=" class='"+e.icon+"' ";if(e.icon&&e.icon.indexOf("/")!==-1)g+=" style='background:url("+
|
||||||
|
e.icon+") center center no-repeat;' ";g+="> </ins><a href='#' rel='"+b+"'>"+e.label;if(e.submenu)g+="<span style='float:right;'>»</span>";g+="</a>";if(e.submenu)if(f=c.vakata.context.parse(e.submenu,true))g+=f;g+="</li>";if(e.separator_after)g+="<li class='vakata-separator vakata-separator-after'></li>"});g+="</ul>";return g.length>10?g:false},exec:function(a){if(c.isFunction(c.vakata.context.func[a])){c.vakata.context.func[a].call(c.vakata.context.data,c.vakata.context.tgt);return true}else return false}};
|
||||||
|
c(function(){c.vakata.css.add_sheet({str:"#vakata-contextmenu { display:none; position:absolute; margin:0; padding:0; min-width:180px; background:#ebebeb; border:1px solid silver; } #vakata-contextmenu ul { min-width:180px; } #vakata-contextmenu ul, #vakata-contextmenu li { margin:0; padding:0; list-style-type:none; display:block; } #vakata-contextmenu li { line-height:20px; min-height:20px; position:relative; padding:0px; } #vakata-contextmenu li a { padding:1px 6px; line-height:17px; display:block; text-decoration:none; margin:1px 1px 0 1px; } #vakata-contextmenu li ins { float:left; width:16px; height:16px; text-decoration:none; margin-right:2px; } #vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a { background:gray; color:white; } #vakata-contextmenu li ul { display:none; position:absolute; top:-2px; left:100%; background:#ebebeb; border:1px solid gray; } #vakata-contextmenu .right { right:100%; left:auto; } #vakata-contextmenu .bottom { bottom:-1px; top:auto; } #vakata-contextmenu li.vakata-separator { min-height:0; height:1px; line-height:1px; font-size:1px; overflow:hidden; margin:0 2px; background:silver; /* border-top:1px solid #fefefe; */ padding:0; } "});
|
||||||
|
c.vakata.context.cnt.delegate("a","click",function(a){a.preventDefault()}).delegate("a","mouseup",function(){c.vakata.context.exec(c(this).attr("rel"))&&c.vakata.context.hide()}).delegate("a","mouseover",function(){c.vakata.context.cnt.find(".vakata-hover").removeClass("vakata-hover")}).appendTo("body");c(document).bind("mousedown",function(a){c.vakata.context.vis&&!c.contains(c.vakata.context.cnt[0],a.target)&&c.vakata.context.hide()});typeof c.hotkeys!=="undefined"&&c(document).bind("keydown","up",
|
||||||
|
function(a){if(c.vakata.context.vis){var d=c.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").prevAll("li:not(.vakata-separator)").first();d.length||(d=c.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").last());d.addClass("vakata-hover");a.stopImmediatePropagation();a.preventDefault()}}).bind("keydown","down",function(a){if(c.vakata.context.vis){var d=c.vakata.context.cnt.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").nextAll("li:not(.vakata-separator)").first();
|
||||||
|
d.length||(d=c.vakata.context.cnt.find("ul:visible").last().children("li:not(.vakata-separator)").first());d.addClass("vakata-hover");a.stopImmediatePropagation();a.preventDefault()}}).bind("keydown","right",function(a){if(c.vakata.context.vis){c.vakata.context.cnt.find(".vakata-hover").children("ul").show().children("li:not(.vakata-separator)").removeClass("vakata-hover").first().addClass("vakata-hover");a.stopImmediatePropagation();a.preventDefault()}}).bind("keydown","left",function(a){if(c.vakata.context.vis){c.vakata.context.cnt.find(".vakata-hover").children("ul").hide().children(".vakata-separator").removeClass("vakata-hover");
|
||||||
|
a.stopImmediatePropagation();a.preventDefault()}}).bind("keydown","esc",function(a){c.vakata.context.hide();a.preventDefault()}).bind("keydown","space",function(a){c.vakata.context.cnt.find(".vakata-hover").last().children("a").click();a.preventDefault()})});c.jstree.plugin("contextmenu",{__init:function(){this.get_container().delegate("a","contextmenu.jstree",c.proxy(function(a){a.preventDefault();this.show_contextmenu(a.currentTarget,a.pageX,a.pageY)},this))},defaults:{show_at_node:true,items:{create:{separator_before:false,
|
||||||
|
separator_after:true,label:"Create",action:function(a){this.create(a)}},rename:{separator_before:false,separator_after:false,label:"Rename",action:function(a){this.rename(a)}},remove:{separator_before:false,icon:false,separator_after:false,label:"Delete",action:function(a){this.remove(a)}},ccp:{separator_before:true,icon:false,separator_after:false,label:"Edit",action:function(a){this.remove(a)},submenu:{cut:{separator_before:false,separator_after:false,label:"Cut",action:function(a){this.cut(a)}},
|
||||||
|
copy:{separator_before:false,icon:false,separator_after:false,label:"Copy",action:function(a){this.copy(a)}},paste:{separator_before:false,icon:false,separator_after:false,label:"Paste",action:function(a){this.paste(a)}}}}}},_fn:{show_contextmenu:function(a,d,g){a=this._get_node(a);var f=this.get_settings().contextmenu,b=a.children("a:visible:eq(0)"),e=false;if(f.show_at_node||typeof d==="undefined"||typeof g==="undefined"){e=b.offset();d=e.left;g=e.top+this.data.core.li_height}if(c.isFunction(f.items))f.items=
|
||||||
|
f.items.call(this,a);c.vakata.context.show(f.items,b,d,g,this);this.data.themes&&c.vakata.context.cnt.attr("class","jstree-"+this.data.themes.theme+"-context")}}})})(jQuery);
|
||||||
|
(function(c){c.jstree.plugin("types",{__init:function(){var a=this.get_settings().types;this.data.types.attach_to=[];this.get_container().bind("init.jstree",c.proxy(function(){var d=a.type_attr,g="",f=this;c.each(a.types,function(b,e){c.each(e,function(h){/^(max_depth|max_children|icon|valid_children)$/.test(h)||f.data.types.attach_to.push(h)});if(!e.icon)return true;if(e.icon.image||e.icon.position){g+=b=="default"?".jstree-"+f.get_index()+" a > .jstree-icon { ":".jstree-"+f.get_index()+" li["+d+
|
||||||
|
"="+b+"] > a > .jstree-icon { ";if(e.icon.image)g+=" background-image:url("+e.icon.image+"); ";g+=e.icon.position?" background-position:"+e.icon.position+"; ":" background-position:0 0; ";g+="} "}});g!=""&&c.vakata.css.add_sheet({str:g})},this)).bind("before.jstree",c.proxy(function(d,g){if(c.inArray(g.func,this.data.types.attach_to)!==-1){var f=this.get_settings().types.types,b=this._get_type(g.args[0]);if(f[b]&&typeof f[b][g.func]!=="undefined"&&!this._check(g.func,g.args[0])){d.stopImmediatePropagation();
|
||||||
|
return false}}},this))},defaults:{max_children:-1,max_depth:-1,valid_children:"all",type_attr:"rel",types:{"default":{max_children:-1,max_depth:-1,valid_children:"all"}}},_fn:{_get_type:function(a){a=this._get_node(a);return!a||!a.length?false:a.attr(this.get_settings().types.type_attr)||"default"},set_type:function(a,d){d=this._get_node(d);return!d.length||!a?false:d.attr(this.get_settings().types.type_attr,a)},_check:function(a,d,g){var f=false,b=this._get_type(d),e=0,h=this,k=this.get_settings().types;
|
||||||
|
if(d===-1)if(k[a])f=k[a];else return;else{if(b===false)return;if(k.types[b]&&k.types[b][a])f=k.types[b][a];else if(k.types["default"]&&k.types["default"][a])f=k.types["default"][a]}if(c.isFunction(f))f=f.call(this,d);a==="max_depth"&&d!==-1&&g!==false&&k.max_depth!==-2&&f!==0&&this._get_node(d).parentsUntil(this.get_container(),"li").each(function(j){e=h._check(a,this,false);if(e!==-1&&e-(j+1)<=0){f=0;return false}if(e>=0&&(e-(j+1)<f||f<0))f=e-(j+1)});return f},check_move:function(){if(!this.__call_old())return false;
|
||||||
|
var a=this._get_move(),d=a.rt.get_settings().types,g=a.rt._check("max_children",a.cr),f=a.rt._check("max_depth",a.cr),b=a.rt._check("valid_children",a.cr),e=0,h=1;if(b==="none")return false;if(c.isArray(b)&&a.ot&&a.ot._get_type){a.o.each(function(){if(c.inArray(a.ot._get_type(this),b)===-1)return h=false});if(h===false)return false}if(d.max_children!==-2&&g!==-1){e=a.cr===-1?this.get_container().children("> ul > li").not(a.o).length:a.cr.children("> ul > li").not(a.o).length;if(e+a.o.length>g)return false}if(d.max_depth!==
|
||||||
|
-2&&f!==-1){h=0;if(f===0)return false;if(typeof a.o.d==="undefined"){for(d=a.o;d.length>0;){d=d.find("> ul > li");h++}a.o.d=h}if(f-a.o.d<0)return false}return true},create_node:function(a,d,g,f,b,e){if(!e&&(b||this._is_loaded(a))){var h=d&&d.match(/^before|after$/i)?this._get_parent(a):this._get_node(a),k=this.get_settings().types,j=this._check("max_children",h),i=this._check("max_depth",h),m=this._check("valid_children",h);g||(g={});if(m==="none")return false;if(c.isArray(m))if(!g.attr||!g.attr[k.type_attr]){if(!g.attr)g.attr=
|
||||||
|
{};g.attr[k.type_attr]=m[0]}else if(c.inArray(g.attr[k.type_attr],m)===-1)return false;if(k.max_children!==-2&&j!==-1){h=h===-1?this.get_container().children("> ul > li").length:h.children("> ul > li").length;if(h+1>j)return false}if(k.max_depth!==-2&&i!==-1&&i-1<=0)return false}return this.__call_old(true,a,d,g,f,b,e)}}})})(jQuery);
|
BIN
scribeengine/public/scripts/jstree/themes/default/d.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
scribeengine/public/scripts/jstree/themes/default/dot_for_ie.gif
Normal file
After Width: | Height: | Size: 43 B |
56
scribeengine/public/scripts/jstree/themes/default/style.css
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* jsTree default theme 1.0
|
||||||
|
* Supported features: dots/no-dots, icons/no-icons, focused, loading
|
||||||
|
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
|
||||||
|
*/
|
||||||
|
|
||||||
|
.jstree-default li,
|
||||||
|
.jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
|
||||||
|
.jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
|
||||||
|
.jstree-default li.jstree-last { background:transparent; }
|
||||||
|
.jstree-default .jstree-open > ins { background-position:-72px 0; }
|
||||||
|
.jstree-default .jstree-closed > ins { background-position:-54px 0; }
|
||||||
|
.jstree-default .jstree-leaf > ins { background-position:-36px 0; }
|
||||||
|
|
||||||
|
.jstree-default .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
|
||||||
|
.jstree-default .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
|
||||||
|
.jstree-default a .jstree-icon { background-position:-56px -19px; }
|
||||||
|
.jstree-default a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
|
||||||
|
|
||||||
|
.jstree-default.jstree-focused { background:#ffffee; }
|
||||||
|
|
||||||
|
.jstree-default .jstree-no-dots li,
|
||||||
|
.jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; }
|
||||||
|
.jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
|
||||||
|
.jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
|
||||||
|
|
||||||
|
.jstree-default .jstree-no-icons a .jstree-icon { display:none; }
|
||||||
|
|
||||||
|
.jstree-default .jstree-search { font-style:italic; }
|
||||||
|
|
||||||
|
.jstree-default .jstree-no-icons .checkbox { display:inline-block; }
|
||||||
|
.jstree-default .jstree-no-checkboxes .checkbox { display:none !important; }
|
||||||
|
.jstree-default .jstree-checked > a > .checkbox { background-position:-38px -19px; }
|
||||||
|
.jstree-default .jstree-unchecked > a > .checkbox { background-position:-2px -19px; }
|
||||||
|
.jstree-default .jstree-undetermined > a > .checkbox { background-position:-20px -19px; }
|
||||||
|
.jstree-default .jstree-checked > a > .checkbox:hover { background-position:-38px -37px; }
|
||||||
|
.jstree-default .jstree-unchecked > a > .checkbox:hover { background-position:-2px -37px; }
|
||||||
|
.jstree-default .jstree-undetermined > a > .checkbox:hover { background-position:-20px -37px; }
|
||||||
|
|
||||||
|
#vakata-dragged.jstree-default ins { background:transparent !important; }
|
||||||
|
#vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
|
||||||
|
#vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
|
||||||
|
#jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; }
|
||||||
|
|
||||||
|
.jstree-default a.jstree-search { color:aqua; }
|
||||||
|
|
||||||
|
#vakata-contextmenu.jstree-default-context,
|
||||||
|
#vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
|
||||||
|
#vakata-contextmenu.jstree-default-context li { }
|
||||||
|
#vakata-contextmenu.jstree-default-context a { color:black; }
|
||||||
|
#vakata-contextmenu.jstree-default-context a:hover,
|
||||||
|
#vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
|
||||||
|
#vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
|
||||||
|
#vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; }
|
||||||
|
|
||||||
|
/* TODO: IE6 support - the `>` selectors */
|
BIN
scribeengine/public/scripts/jstree/themes/default/throbber.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
167
scribeengine/public/scripts/tinymce/langs/en.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
tinyMCE.addI18n({en:{
|
||||||
|
common:{
|
||||||
|
edit_confirm:"Do you want to use the WYSIWYG mode for this textarea?",
|
||||||
|
apply:"Apply",
|
||||||
|
insert:"Insert",
|
||||||
|
update:"Update",
|
||||||
|
cancel:"Cancel",
|
||||||
|
close:"Close",
|
||||||
|
browse:"Browse",
|
||||||
|
class_name:"Class",
|
||||||
|
not_set:"-- Not set --",
|
||||||
|
clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?",
|
||||||
|
clipboard_no_support:"Currently not supported by your browser, use keyboard shortcuts instead.",
|
||||||
|
popup_blocked:"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.",
|
||||||
|
invalid_data:"Error: Invalid values entered, these are marked in red.",
|
||||||
|
more_colors:"More colors"
|
||||||
|
},
|
||||||
|
contextmenu:{
|
||||||
|
align:"Alignment",
|
||||||
|
left:"Left",
|
||||||
|
center:"Center",
|
||||||
|
right:"Right",
|
||||||
|
full:"Full"
|
||||||
|
},
|
||||||
|
insertdatetime:{
|
||||||
|
date_fmt:"%Y-%m-%d",
|
||||||
|
time_fmt:"%H:%M:%S",
|
||||||
|
insertdate_desc:"Insert date",
|
||||||
|
inserttime_desc:"Insert time",
|
||||||
|
months_long:"January,February,March,April,May,June,July,August,September,October,November,December",
|
||||||
|
months_short:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
|
||||||
|
day_long:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday",
|
||||||
|
day_short:"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun"
|
||||||
|
},
|
||||||
|
print:{
|
||||||
|
print_desc:"Print"
|
||||||
|
},
|
||||||
|
preview:{
|
||||||
|
preview_desc:"Preview"
|
||||||
|
},
|
||||||
|
directionality:{
|
||||||
|
ltr_desc:"Direction left to right",
|
||||||
|
rtl_desc:"Direction right to left"
|
||||||
|
},
|
||||||
|
layer:{
|
||||||
|
insertlayer_desc:"Insert new layer",
|
||||||
|
forward_desc:"Move forward",
|
||||||
|
backward_desc:"Move backward",
|
||||||
|
absolute_desc:"Toggle absolute positioning",
|
||||||
|
content:"New layer..."
|
||||||
|
},
|
||||||
|
save:{
|
||||||
|
save_desc:"Save",
|
||||||
|
cancel_desc:"Cancel all changes"
|
||||||
|
},
|
||||||
|
nonbreaking:{
|
||||||
|
nonbreaking_desc:"Insert non-breaking space character"
|
||||||
|
},
|
||||||
|
iespell:{
|
||||||
|
iespell_desc:"Run spell checking",
|
||||||
|
download:"ieSpell not detected. Do you want to install it now?"
|
||||||
|
},
|
||||||
|
advhr:{
|
||||||
|
advhr_desc:"Horizontal rule"
|
||||||
|
},
|
||||||
|
emotions:{
|
||||||
|
emotions_desc:"Emotions"
|
||||||
|
},
|
||||||
|
searchreplace:{
|
||||||
|
search_desc:"Find",
|
||||||
|
replace_desc:"Find/Replace"
|
||||||
|
},
|
||||||
|
advimage:{
|
||||||
|
image_desc:"Insert/edit image"
|
||||||
|
},
|
||||||
|
advlink:{
|
||||||
|
link_desc:"Insert/edit link"
|
||||||
|
},
|
||||||
|
xhtmlxtras:{
|
||||||
|
cite_desc:"Citation",
|
||||||
|
abbr_desc:"Abbreviation",
|
||||||
|
acronym_desc:"Acronym",
|
||||||
|
del_desc:"Deletion",
|
||||||
|
ins_desc:"Insertion",
|
||||||
|
attribs_desc:"Insert/Edit Attributes"
|
||||||
|
},
|
||||||
|
style:{
|
||||||
|
desc:"Edit CSS Style"
|
||||||
|
},
|
||||||
|
paste:{
|
||||||
|
paste_text_desc:"Paste as Plain Text",
|
||||||
|
paste_word_desc:"Paste from Word",
|
||||||
|
selectall_desc:"Select All"
|
||||||
|
},
|
||||||
|
paste_dlg:{
|
||||||
|
text_title:"Use CTRL+V on your keyboard to paste the text into the window.",
|
||||||
|
text_linebreaks:"Keep linebreaks",
|
||||||
|
word_title:"Use CTRL+V on your keyboard to paste the text into the window."
|
||||||
|
},
|
||||||
|
table:{
|
||||||
|
desc:"Inserts a new table",
|
||||||
|
row_before_desc:"Insert row before",
|
||||||
|
row_after_desc:"Insert row after",
|
||||||
|
delete_row_desc:"Delete row",
|
||||||
|
col_before_desc:"Insert column before",
|
||||||
|
col_after_desc:"Insert column after",
|
||||||
|
delete_col_desc:"Remove column",
|
||||||
|
split_cells_desc:"Split merged table cells",
|
||||||
|
merge_cells_desc:"Merge table cells",
|
||||||
|
row_desc:"Table row properties",
|
||||||
|
cell_desc:"Table cell properties",
|
||||||
|
props_desc:"Table properties",
|
||||||
|
paste_row_before_desc:"Paste table row before",
|
||||||
|
paste_row_after_desc:"Paste table row after",
|
||||||
|
cut_row_desc:"Cut table row",
|
||||||
|
copy_row_desc:"Copy table row",
|
||||||
|
del:"Delete table",
|
||||||
|
row:"Row",
|
||||||
|
col:"Column",
|
||||||
|
cell:"Cell"
|
||||||
|
},
|
||||||
|
autosave:{
|
||||||
|
unload_msg:"The changes you made will be lost if you navigate away from this page."
|
||||||
|
},
|
||||||
|
fullscreen:{
|
||||||
|
desc:"Toggle fullscreen mode"
|
||||||
|
},
|
||||||
|
media:{
|
||||||
|
desc:"Insert / edit embedded media",
|
||||||
|
edit:"Edit embedded media"
|
||||||
|
},
|
||||||
|
fullpage:{
|
||||||
|
desc:"Document properties"
|
||||||
|
},
|
||||||
|
template:{
|
||||||
|
desc:"Insert predefined template content"
|
||||||
|
},
|
||||||
|
visualchars:{
|
||||||
|
desc:"Visual control characters on/off."
|
||||||
|
},
|
||||||
|
spellchecker:{
|
||||||
|
desc:"Toggle spellchecker",
|
||||||
|
menu:"Spellchecker settings",
|
||||||
|
ignore_word:"Ignore word",
|
||||||
|
ignore_words:"Ignore all",
|
||||||
|
langs:"Languages",
|
||||||
|
wait:"Please wait...",
|
||||||
|
sug:"Suggestions",
|
||||||
|
no_sug:"No suggestions",
|
||||||
|
no_mpell:"No misspellings found."
|
||||||
|
},
|
||||||
|
pagebreak:{
|
||||||
|
desc:"Insert page break."
|
||||||
|
},
|
||||||
|
advlist : {
|
||||||
|
types : 'Types',
|
||||||
|
def : 'Default',
|
||||||
|
lower_alpha : "Lower alpha",
|
||||||
|
lower_greek : "Lower greek",
|
||||||
|
lower_roman : "Lower roman",
|
||||||
|
upper_alpha : "Upper alpha",
|
||||||
|
upper_roman : "Upper roman",
|
||||||
|
circle : "Circle",
|
||||||
|
disc : "Disc",
|
||||||
|
square : "Square"
|
||||||
|
}
|
||||||
|
}});
|
504
scribeengine/public/scripts/tinymce/license.txt
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
input.radio {border:1px none #000; background:transparent; vertical-align:middle;}
|
||||||
|
.panel_wrapper div.current {height:80px;}
|
||||||
|
#width {width:50px; vertical-align:middle;}
|
||||||
|
#width2 {width:50px; vertical-align:middle;}
|
||||||
|
#size {width:100px;}
|
@ -0,0 +1 @@
|
|||||||
|
(function(){tinymce.create("tinymce.plugins.AdvancedHRPlugin",{init:function(a,b){a.addCommand("mceAdvancedHr",function(){a.windowManager.open({file:b+"/rule.htm",width:250+parseInt(a.getLang("advhr.delta_width",0)),height:160+parseInt(a.getLang("advhr.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("advhr",{title:"advhr.advhr_desc",cmd:"mceAdvancedHr"});a.onNodeChange.add(function(d,c,e){c.setActive("advhr",e.nodeName=="HR")});a.onClick.add(function(c,d){d=d.target;if(d.nodeName==="HR"){c.selection.select(d)}})},getInfo:function(){return{longname:"Advanced HR",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advhr",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advhr",tinymce.plugins.AdvancedHRPlugin)})();
|
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
tinymce.create('tinymce.plugins.AdvancedHRPlugin', {
|
||||||
|
init : function(ed, url) {
|
||||||
|
// Register commands
|
||||||
|
ed.addCommand('mceAdvancedHr', function() {
|
||||||
|
ed.windowManager.open({
|
||||||
|
file : url + '/rule.htm',
|
||||||
|
width : 250 + parseInt(ed.getLang('advhr.delta_width', 0)),
|
||||||
|
height : 160 + parseInt(ed.getLang('advhr.delta_height', 0)),
|
||||||
|
inline : 1
|
||||||
|
}, {
|
||||||
|
plugin_url : url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register buttons
|
||||||
|
ed.addButton('advhr', {
|
||||||
|
title : 'advhr.advhr_desc',
|
||||||
|
cmd : 'mceAdvancedHr'
|
||||||
|
});
|
||||||
|
|
||||||
|
ed.onNodeChange.add(function(ed, cm, n) {
|
||||||
|
cm.setActive('advhr', n.nodeName == 'HR');
|
||||||
|
});
|
||||||
|
|
||||||
|
ed.onClick.add(function(ed, e) {
|
||||||
|
e = e.target;
|
||||||
|
|
||||||
|
if (e.nodeName === 'HR')
|
||||||
|
ed.selection.select(e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Advanced HR',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advhr',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
tinymce.PluginManager.add('advhr', tinymce.plugins.AdvancedHRPlugin);
|
||||||
|
})();
|
43
scribeengine/public/scripts/tinymce/plugins/advhr/js/rule.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
var AdvHRDialog = {
|
||||||
|
init : function(ed) {
|
||||||
|
var dom = ed.dom, f = document.forms[0], n = ed.selection.getNode(), w;
|
||||||
|
|
||||||
|
w = dom.getAttrib(n, 'width');
|
||||||
|
f.width.value = w ? parseInt(w) : (dom.getStyle('width') || '');
|
||||||
|
f.size.value = dom.getAttrib(n, 'size') || parseInt(dom.getStyle('height')) || '';
|
||||||
|
f.noshade.checked = !!dom.getAttrib(n, 'noshade') || !!dom.getStyle('border-width');
|
||||||
|
selectByValue(f, 'width2', w.indexOf('%') != -1 ? '%' : 'px');
|
||||||
|
},
|
||||||
|
|
||||||
|
update : function() {
|
||||||
|
var ed = tinyMCEPopup.editor, h, f = document.forms[0], st = '';
|
||||||
|
|
||||||
|
h = '<hr';
|
||||||
|
|
||||||
|
if (f.size.value) {
|
||||||
|
h += ' size="' + f.size.value + '"';
|
||||||
|
st += ' height:' + f.size.value + 'px;';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.width.value) {
|
||||||
|
h += ' width="' + f.width.value + (f.width2.value == '%' ? '%' : '') + '"';
|
||||||
|
st += ' width:' + f.width.value + (f.width2.value == '%' ? '%' : 'px') + ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.noshade.checked) {
|
||||||
|
h += ' noshade="noshade"';
|
||||||
|
st += ' border-width: 1px; border-style: solid; border-color: #CCCCCC; color: #ffffff;';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ed.settings.inline_styles)
|
||||||
|
h += ' style="' + tinymce.trim(st) + '"';
|
||||||
|
|
||||||
|
h += ' />';
|
||||||
|
|
||||||
|
ed.execCommand("mceInsertContent", false, h);
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tinyMCEPopup.requireLangPack();
|
||||||
|
tinyMCEPopup.onInit.add(AdvHRDialog.init, AdvHRDialog);
|
@ -0,0 +1,5 @@
|
|||||||
|
tinyMCE.addI18n('en.advhr_dlg',{
|
||||||
|
width:"Width",
|
||||||
|
size:"Height",
|
||||||
|
noshade:"No shadow"
|
||||||
|
});
|
62
scribeengine/public/scripts/tinymce/plugins/advhr/rule.htm
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{#advhr.advhr_desc}</title>
|
||||||
|
<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
|
||||||
|
<script type="text/javascript" src="js/rule.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/mctabs.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/form_utils.js"></script>
|
||||||
|
<link href="css/advhr.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form onsubmit="AdvHRDialog.update();return false;" action="#">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advhr.advhr_desc}</a></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel_wrapper">
|
||||||
|
<div id="general_panel" class="panel current">
|
||||||
|
<table border="0" cellpadding="4" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td><label for="width">{#advhr_dlg.width}</label></td>
|
||||||
|
<td class="nowrap">
|
||||||
|
<input id="width" name="width" type="text" value="" class="mceFocus" />
|
||||||
|
<select name="width2" id="width2">
|
||||||
|
<option value="">px</option>
|
||||||
|
<option value="%">%</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="size">{#advhr_dlg.size}</label></td>
|
||||||
|
<td><select id="size" name="size">
|
||||||
|
<option value="">Normal</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="3">3</option>
|
||||||
|
<option value="4">4</option>
|
||||||
|
<option value="5">5</option>
|
||||||
|
</select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="noshade">{#advhr_dlg.noshade}</label></td>
|
||||||
|
<td><input type="checkbox" name="noshade" id="noshade" class="radio" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mceActionPanel">
|
||||||
|
<div style="float: left">
|
||||||
|
<input type="submit" id="insert" name="insert" value="{#insert}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="float: right">
|
||||||
|
<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,13 @@
|
|||||||
|
#src_list, #over_list, #out_list {width:280px;}
|
||||||
|
.mceActionPanel {margin-top:7px;}
|
||||||
|
.alignPreview {border:1px solid #000; width:140px; height:140px; overflow:hidden; padding:5px;}
|
||||||
|
.checkbox {border:0;}
|
||||||
|
.panel_wrapper div.current {height:305px;}
|
||||||
|
#prev {margin:0; border:1px solid #000; width:428px; height:150px; overflow:auto;}
|
||||||
|
#align, #classlist {width:150px;}
|
||||||
|
#width, #height {vertical-align:middle; width:50px; text-align:center;}
|
||||||
|
#vspace, #hspace, #border {vertical-align:middle; width:30px; text-align:center;}
|
||||||
|
#class_list {width:180px;}
|
||||||
|
input {width: 280px;}
|
||||||
|
#constrain, #onmousemovecheck {width:auto;}
|
||||||
|
#id, #dir, #lang, #usemap, #longdesc {width:200px;}
|
@ -0,0 +1 @@
|
|||||||
|
(function(){tinymce.create("tinymce.plugins.AdvancedImagePlugin",{init:function(a,b){a.addCommand("mceAdvImage",function(){if(a.dom.getAttrib(a.selection.getNode(),"class").indexOf("mceItem")!=-1){return}a.windowManager.open({file:b+"/image.htm",width:480+parseInt(a.getLang("advimage.delta_width",0)),height:385+parseInt(a.getLang("advimage.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("image",{title:"advimage.image_desc",cmd:"mceAdvImage"})},getInfo:function(){return{longname:"Advanced image",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advimage",tinymce.plugins.AdvancedImagePlugin)})();
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
tinymce.create('tinymce.plugins.AdvancedImagePlugin', {
|
||||||
|
init : function(ed, url) {
|
||||||
|
// Register commands
|
||||||
|
ed.addCommand('mceAdvImage', function() {
|
||||||
|
// Internal image object like a flash placeholder
|
||||||
|
if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ed.windowManager.open({
|
||||||
|
file : url + '/image.htm',
|
||||||
|
width : 480 + parseInt(ed.getLang('advimage.delta_width', 0)),
|
||||||
|
height : 385 + parseInt(ed.getLang('advimage.delta_height', 0)),
|
||||||
|
inline : 1
|
||||||
|
}, {
|
||||||
|
plugin_url : url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register buttons
|
||||||
|
ed.addButton('image', {
|
||||||
|
title : 'advimage.image_desc',
|
||||||
|
cmd : 'mceAdvImage'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Advanced image',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
tinymce.PluginManager.add('advimage', tinymce.plugins.AdvancedImagePlugin);
|
||||||
|
})();
|
237
scribeengine/public/scripts/tinymce/plugins/advimage/image.htm
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{#advimage_dlg.dialog_title}</title>
|
||||||
|
<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/mctabs.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/form_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/validate.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/editable_selects.js"></script>
|
||||||
|
<script type="text/javascript" src="js/image.js"></script>
|
||||||
|
<link href="css/advimage.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body id="advimage" style="display: none">
|
||||||
|
<form onsubmit="ImageDialog.insert();return false;" action="#">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advimage_dlg.tab_general}</a></span></li>
|
||||||
|
<li id="appearance_tab"><span><a href="javascript:mcTabs.displayTab('appearance_tab','appearance_panel');" onmousedown="return false;">{#advimage_dlg.tab_appearance}</a></span></li>
|
||||||
|
<li id="advanced_tab"><span><a href="javascript:mcTabs.displayTab('advanced_tab','advanced_panel');" onmousedown="return false;">{#advimage_dlg.tab_advanced}</a></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel_wrapper">
|
||||||
|
<div id="general_panel" class="panel current">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advimage_dlg.general}</legend>
|
||||||
|
|
||||||
|
<table class="properties">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="srclabel" for="src">{#advimage_dlg.src}</label></td>
|
||||||
|
<td colspan="2"><table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input name="src" type="text" id="src" value="" class="mceFocus" onchange="ImageDialog.showPreviewImage(this.value);" /></td>
|
||||||
|
<td id="srcbrowsercontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="src_list">{#advimage_dlg.image_list}</label></td>
|
||||||
|
<td><select id="src_list" name="src_list" onchange="document.getElementById('src').value=this.options[this.selectedIndex].value;document.getElementById('alt').value=this.options[this.selectedIndex].text;document.getElementById('title').value=this.options[this.selectedIndex].text;ImageDialog.showPreviewImage(this.options[this.selectedIndex].value);"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="altlabel" for="alt">{#advimage_dlg.alt}</label></td>
|
||||||
|
<td colspan="2"><input id="alt" name="alt" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="titlelabel" for="title">{#advimage_dlg.title}</label></td>
|
||||||
|
<td colspan="2"><input id="title" name="title" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advimage_dlg.preview}</legend>
|
||||||
|
<div id="prev"></div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="appearance_panel" class="panel">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advimage_dlg.tab_appearance}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="4" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="alignlabel" for="align">{#advimage_dlg.align}</label></td>
|
||||||
|
<td><select id="align" name="align" onchange="ImageDialog.updateStyle('align');ImageDialog.changeAppearance();">
|
||||||
|
<option value="">{#not_set}</option>
|
||||||
|
<option value="baseline">{#advimage_dlg.align_baseline}</option>
|
||||||
|
<option value="top">{#advimage_dlg.align_top}</option>
|
||||||
|
<option value="middle">{#advimage_dlg.align_middle}</option>
|
||||||
|
<option value="bottom">{#advimage_dlg.align_bottom}</option>
|
||||||
|
<option value="text-top">{#advimage_dlg.align_texttop}</option>
|
||||||
|
<option value="text-bottom">{#advimage_dlg.align_textbottom}</option>
|
||||||
|
<option value="left">{#advimage_dlg.align_left}</option>
|
||||||
|
<option value="right">{#advimage_dlg.align_right}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td rowspan="6" valign="top">
|
||||||
|
<div class="alignPreview">
|
||||||
|
<img id="alignSampleImg" src="img/sample.gif" alt="{#advimage_dlg.example_img}" />
|
||||||
|
Lorem ipsum, Dolor sit amet, consectetuer adipiscing loreum ipsum edipiscing elit, sed diam
|
||||||
|
nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.Loreum ipsum
|
||||||
|
edipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam
|
||||||
|
erat volutpat.
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="widthlabel" for="width">{#advimage_dlg.dimensions}</label></td>
|
||||||
|
<td class="nowrap">
|
||||||
|
<input name="width" type="text" id="width" value="" size="5" maxlength="5" class="size" onchange="ImageDialog.changeHeight();" /> x
|
||||||
|
<input name="height" type="text" id="height" value="" size="5" maxlength="5" class="size" onchange="ImageDialog.changeWidth();" /> px
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td><table border="0" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td><input id="constrain" type="checkbox" name="constrain" class="checkbox" /></td>
|
||||||
|
<td><label id="constrainlabel" for="constrain">{#advimage_dlg.constrain_proportions}</label></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="vspacelabel" for="vspace">{#advimage_dlg.vspace}</label></td>
|
||||||
|
<td><input name="vspace" type="text" id="vspace" value="" size="3" maxlength="3" class="number" onchange="ImageDialog.updateStyle('vspace');ImageDialog.changeAppearance();" onblur="ImageDialog.updateStyle('vspace');ImageDialog.changeAppearance();" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="hspacelabel" for="hspace">{#advimage_dlg.hspace}</label></td>
|
||||||
|
<td><input name="hspace" type="text" id="hspace" value="" size="3" maxlength="3" class="number" onchange="ImageDialog.updateStyle('hspace');ImageDialog.changeAppearance();" onblur="ImageDialog.updateStyle('hspace');ImageDialog.changeAppearance();" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="borderlabel" for="border">{#advimage_dlg.border}</label></td>
|
||||||
|
<td><input id="border" name="border" type="text" value="" size="3" maxlength="3" class="number" onchange="ImageDialog.updateStyle('border');ImageDialog.changeAppearance();" onblur="ImageDialog.updateStyle('border');ImageDialog.changeAppearance();" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label for="class_list">{#class_name}</label></td>
|
||||||
|
<td colspan="2"><select id="class_list" name="class_list" class="mceEditableSelect"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="stylelabel" for="style">{#advimage_dlg.style}</label></td>
|
||||||
|
<td colspan="2"><input id="style" name="style" type="text" value="" onchange="ImageDialog.changeAppearance();" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- <tr>
|
||||||
|
<td class="column1"><label id="classeslabel" for="classes">{#advimage_dlg.classes}</label></td>
|
||||||
|
<td colspan="2"><input id="classes" name="classes" type="text" value="" onchange="selectByValue(this.form,'classlist',this.value,true);" /></td>
|
||||||
|
</tr> -->
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="advanced_panel" class="panel">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advimage_dlg.swap_image}</legend>
|
||||||
|
|
||||||
|
<input type="checkbox" id="onmousemovecheck" name="onmousemovecheck" class="checkbox" onclick="ImageDialog.setSwapImage(this.checked);" />
|
||||||
|
<label id="onmousemovechecklabel" for="onmousemovecheck">{#advimage_dlg.alt_image}</label>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="4" cellspacing="0" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="onmouseoversrclabel" for="onmouseoversrc">{#advimage_dlg.mouseover}</label></td>
|
||||||
|
<td><table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input id="onmouseoversrc" name="onmouseoversrc" type="text" value="" /></td>
|
||||||
|
<td id="onmouseoversrccontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="over_list">{#advimage_dlg.image_list}</label></td>
|
||||||
|
<td><select id="over_list" name="over_list" onchange="document.getElementById('onmouseoversrc').value=this.options[this.selectedIndex].value;"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="onmouseoutsrclabel" for="onmouseoutsrc">{#advimage_dlg.mouseout}</label></td>
|
||||||
|
<td class="column2"><table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input id="onmouseoutsrc" name="onmouseoutsrc" type="text" value="" /></td>
|
||||||
|
<td id="onmouseoutsrccontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="out_list">{#advimage_dlg.image_list}</label></td>
|
||||||
|
<td><select id="out_list" name="out_list" onchange="document.getElementById('onmouseoutsrc').value=this.options[this.selectedIndex].value;"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advimage_dlg.misc}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="4" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="idlabel" for="id">{#advimage_dlg.id}</label></td>
|
||||||
|
<td><input id="id" name="id" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="dirlabel" for="dir">{#advimage_dlg.langdir}</label></td>
|
||||||
|
<td>
|
||||||
|
<select id="dir" name="dir" onchange="ImageDialog.changeAppearance();">
|
||||||
|
<option value="">{#not_set}</option>
|
||||||
|
<option value="ltr">{#advimage_dlg.ltr}</option>
|
||||||
|
<option value="rtl">{#advimage_dlg.rtl}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="langlabel" for="lang">{#advimage_dlg.langcode}</label></td>
|
||||||
|
<td>
|
||||||
|
<input id="lang" name="lang" type="text" value="" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="usemaplabel" for="usemap">{#advimage_dlg.map}</label></td>
|
||||||
|
<td>
|
||||||
|
<input id="usemap" name="usemap" type="text" value="" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="longdesclabel" for="longdesc">{#advimage_dlg.long_desc}</label></td>
|
||||||
|
<td><table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input id="longdesc" name="longdesc" type="text" value="" /></td>
|
||||||
|
<td id="longdesccontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mceActionPanel">
|
||||||
|
<div style="float: left">
|
||||||
|
<input type="submit" id="insert" name="insert" value="{#insert}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="float: right">
|
||||||
|
<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
After Width: | Height: | Size: 1.6 KiB |
443
scribeengine/public/scripts/tinymce/plugins/advimage/js/image.js
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
var ImageDialog = {
|
||||||
|
preInit : function() {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
tinyMCEPopup.requireLangPack();
|
||||||
|
|
||||||
|
if (url = tinyMCEPopup.getParam("external_image_list_url"))
|
||||||
|
document.write('<script language="javascript" type="text/javascript" src="' + tinyMCEPopup.editor.documentBaseURI.toAbsolute(url) + '"></script>');
|
||||||
|
},
|
||||||
|
|
||||||
|
init : function(ed) {
|
||||||
|
var f = document.forms[0], nl = f.elements, ed = tinyMCEPopup.editor, dom = ed.dom, n = ed.selection.getNode();
|
||||||
|
|
||||||
|
tinyMCEPopup.resizeToInnerSize();
|
||||||
|
this.fillClassList('class_list');
|
||||||
|
this.fillFileList('src_list', 'tinyMCEImageList');
|
||||||
|
this.fillFileList('over_list', 'tinyMCEImageList');
|
||||||
|
this.fillFileList('out_list', 'tinyMCEImageList');
|
||||||
|
TinyMCE_EditableSelects.init();
|
||||||
|
|
||||||
|
if (n.nodeName == 'IMG') {
|
||||||
|
nl.src.value = dom.getAttrib(n, 'src');
|
||||||
|
nl.width.value = dom.getAttrib(n, 'width');
|
||||||
|
nl.height.value = dom.getAttrib(n, 'height');
|
||||||
|
nl.alt.value = dom.getAttrib(n, 'alt');
|
||||||
|
nl.title.value = dom.getAttrib(n, 'title');
|
||||||
|
nl.vspace.value = this.getAttrib(n, 'vspace');
|
||||||
|
nl.hspace.value = this.getAttrib(n, 'hspace');
|
||||||
|
nl.border.value = this.getAttrib(n, 'border');
|
||||||
|
selectByValue(f, 'align', this.getAttrib(n, 'align'));
|
||||||
|
selectByValue(f, 'class_list', dom.getAttrib(n, 'class'), true, true);
|
||||||
|
nl.style.value = dom.getAttrib(n, 'style');
|
||||||
|
nl.id.value = dom.getAttrib(n, 'id');
|
||||||
|
nl.dir.value = dom.getAttrib(n, 'dir');
|
||||||
|
nl.lang.value = dom.getAttrib(n, 'lang');
|
||||||
|
nl.usemap.value = dom.getAttrib(n, 'usemap');
|
||||||
|
nl.longdesc.value = dom.getAttrib(n, 'longdesc');
|
||||||
|
nl.insert.value = ed.getLang('update');
|
||||||
|
|
||||||
|
if (/^\s*this.src\s*=\s*\'([^\']+)\';?\s*$/.test(dom.getAttrib(n, 'onmouseover')))
|
||||||
|
nl.onmouseoversrc.value = dom.getAttrib(n, 'onmouseover').replace(/^\s*this.src\s*=\s*\'([^\']+)\';?\s*$/, '$1');
|
||||||
|
|
||||||
|
if (/^\s*this.src\s*=\s*\'([^\']+)\';?\s*$/.test(dom.getAttrib(n, 'onmouseout')))
|
||||||
|
nl.onmouseoutsrc.value = dom.getAttrib(n, 'onmouseout').replace(/^\s*this.src\s*=\s*\'([^\']+)\';?\s*$/, '$1');
|
||||||
|
|
||||||
|
if (ed.settings.inline_styles) {
|
||||||
|
// Move attribs to styles
|
||||||
|
if (dom.getAttrib(n, 'align'))
|
||||||
|
this.updateStyle('align');
|
||||||
|
|
||||||
|
if (dom.getAttrib(n, 'hspace'))
|
||||||
|
this.updateStyle('hspace');
|
||||||
|
|
||||||
|
if (dom.getAttrib(n, 'border'))
|
||||||
|
this.updateStyle('border');
|
||||||
|
|
||||||
|
if (dom.getAttrib(n, 'vspace'))
|
||||||
|
this.updateStyle('vspace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup browse button
|
||||||
|
document.getElementById('srcbrowsercontainer').innerHTML = getBrowserHTML('srcbrowser','src','image','theme_advanced_image');
|
||||||
|
if (isVisible('srcbrowser'))
|
||||||
|
document.getElementById('src').style.width = '260px';
|
||||||
|
|
||||||
|
// Setup browse button
|
||||||
|
document.getElementById('onmouseoversrccontainer').innerHTML = getBrowserHTML('overbrowser','onmouseoversrc','image','theme_advanced_image');
|
||||||
|
if (isVisible('overbrowser'))
|
||||||
|
document.getElementById('onmouseoversrc').style.width = '260px';
|
||||||
|
|
||||||
|
// Setup browse button
|
||||||
|
document.getElementById('onmouseoutsrccontainer').innerHTML = getBrowserHTML('outbrowser','onmouseoutsrc','image','theme_advanced_image');
|
||||||
|
if (isVisible('outbrowser'))
|
||||||
|
document.getElementById('onmouseoutsrc').style.width = '260px';
|
||||||
|
|
||||||
|
// If option enabled default contrain proportions to checked
|
||||||
|
if (ed.getParam("advimage_constrain_proportions", true))
|
||||||
|
f.constrain.checked = true;
|
||||||
|
|
||||||
|
// Check swap image if valid data
|
||||||
|
if (nl.onmouseoversrc.value || nl.onmouseoutsrc.value)
|
||||||
|
this.setSwapImage(true);
|
||||||
|
else
|
||||||
|
this.setSwapImage(false);
|
||||||
|
|
||||||
|
this.changeAppearance();
|
||||||
|
this.showPreviewImage(nl.src.value, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
insert : function(file, title) {
|
||||||
|
var ed = tinyMCEPopup.editor, t = this, f = document.forms[0];
|
||||||
|
|
||||||
|
if (f.src.value === '') {
|
||||||
|
if (ed.selection.getNode().nodeName == 'IMG') {
|
||||||
|
ed.dom.remove(ed.selection.getNode());
|
||||||
|
ed.execCommand('mceRepaint');
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tinyMCEPopup.getParam("accessibility_warnings", 1)) {
|
||||||
|
if (!f.alt.value) {
|
||||||
|
tinyMCEPopup.confirm(tinyMCEPopup.getLang('advimage_dlg.missing_alt'), function(s) {
|
||||||
|
if (s)
|
||||||
|
t.insertAndClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.insertAndClose();
|
||||||
|
},
|
||||||
|
|
||||||
|
insertAndClose : function() {
|
||||||
|
var ed = tinyMCEPopup.editor, f = document.forms[0], nl = f.elements, v, args = {}, el;
|
||||||
|
|
||||||
|
tinyMCEPopup.restoreSelection();
|
||||||
|
|
||||||
|
// Fixes crash in Safari
|
||||||
|
if (tinymce.isWebKit)
|
||||||
|
ed.getWin().focus();
|
||||||
|
|
||||||
|
if (!ed.settings.inline_styles) {
|
||||||
|
args = {
|
||||||
|
vspace : nl.vspace.value,
|
||||||
|
hspace : nl.hspace.value,
|
||||||
|
border : nl.border.value,
|
||||||
|
align : getSelectValue(f, 'align')
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Remove deprecated values
|
||||||
|
args = {
|
||||||
|
vspace : '',
|
||||||
|
hspace : '',
|
||||||
|
border : '',
|
||||||
|
align : ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tinymce.extend(args, {
|
||||||
|
src : nl.src.value,
|
||||||
|
width : nl.width.value,
|
||||||
|
height : nl.height.value,
|
||||||
|
alt : nl.alt.value,
|
||||||
|
title : nl.title.value,
|
||||||
|
'class' : getSelectValue(f, 'class_list'),
|
||||||
|
style : nl.style.value,
|
||||||
|
id : nl.id.value,
|
||||||
|
dir : nl.dir.value,
|
||||||
|
lang : nl.lang.value,
|
||||||
|
usemap : nl.usemap.value,
|
||||||
|
longdesc : nl.longdesc.value
|
||||||
|
});
|
||||||
|
|
||||||
|
args.onmouseover = args.onmouseout = '';
|
||||||
|
|
||||||
|
if (f.onmousemovecheck.checked) {
|
||||||
|
if (nl.onmouseoversrc.value)
|
||||||
|
args.onmouseover = "this.src='" + nl.onmouseoversrc.value + "';";
|
||||||
|
|
||||||
|
if (nl.onmouseoutsrc.value)
|
||||||
|
args.onmouseout = "this.src='" + nl.onmouseoutsrc.value + "';";
|
||||||
|
}
|
||||||
|
|
||||||
|
el = ed.selection.getNode();
|
||||||
|
|
||||||
|
if (el && el.nodeName == 'IMG') {
|
||||||
|
ed.dom.setAttribs(el, args);
|
||||||
|
} else {
|
||||||
|
ed.execCommand('mceInsertContent', false, '<img id="__mce_tmp" />', {skip_undo : 1});
|
||||||
|
ed.dom.setAttribs('__mce_tmp', args);
|
||||||
|
ed.dom.setAttrib('__mce_tmp', 'id', '');
|
||||||
|
ed.undoManager.add();
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
getAttrib : function(e, at) {
|
||||||
|
var ed = tinyMCEPopup.editor, dom = ed.dom, v, v2;
|
||||||
|
|
||||||
|
if (ed.settings.inline_styles) {
|
||||||
|
switch (at) {
|
||||||
|
case 'align':
|
||||||
|
if (v = dom.getStyle(e, 'float'))
|
||||||
|
return v;
|
||||||
|
|
||||||
|
if (v = dom.getStyle(e, 'vertical-align'))
|
||||||
|
return v;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'hspace':
|
||||||
|
v = dom.getStyle(e, 'margin-left')
|
||||||
|
v2 = dom.getStyle(e, 'margin-right');
|
||||||
|
|
||||||
|
if (v && v == v2)
|
||||||
|
return parseInt(v.replace(/[^0-9]/g, ''));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'vspace':
|
||||||
|
v = dom.getStyle(e, 'margin-top')
|
||||||
|
v2 = dom.getStyle(e, 'margin-bottom');
|
||||||
|
if (v && v == v2)
|
||||||
|
return parseInt(v.replace(/[^0-9]/g, ''));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'border':
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
tinymce.each(['top', 'right', 'bottom', 'left'], function(sv) {
|
||||||
|
sv = dom.getStyle(e, 'border-' + sv + '-width');
|
||||||
|
|
||||||
|
// False or not the same as prev
|
||||||
|
if (!sv || (sv != v && v !== 0)) {
|
||||||
|
v = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sv)
|
||||||
|
v = sv;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (v)
|
||||||
|
return parseInt(v.replace(/[^0-9]/g, ''));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v = dom.getAttrib(e, at))
|
||||||
|
return v;
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
setSwapImage : function(st) {
|
||||||
|
var f = document.forms[0];
|
||||||
|
|
||||||
|
f.onmousemovecheck.checked = st;
|
||||||
|
setBrowserDisabled('overbrowser', !st);
|
||||||
|
setBrowserDisabled('outbrowser', !st);
|
||||||
|
|
||||||
|
if (f.over_list)
|
||||||
|
f.over_list.disabled = !st;
|
||||||
|
|
||||||
|
if (f.out_list)
|
||||||
|
f.out_list.disabled = !st;
|
||||||
|
|
||||||
|
f.onmouseoversrc.disabled = !st;
|
||||||
|
f.onmouseoutsrc.disabled = !st;
|
||||||
|
},
|
||||||
|
|
||||||
|
fillClassList : function(id) {
|
||||||
|
var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl;
|
||||||
|
|
||||||
|
if (v = tinyMCEPopup.getParam('theme_advanced_styles')) {
|
||||||
|
cl = [];
|
||||||
|
|
||||||
|
tinymce.each(v.split(';'), function(v) {
|
||||||
|
var p = v.split('=');
|
||||||
|
|
||||||
|
cl.push({'title' : p[0], 'class' : p[1]});
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
cl = tinyMCEPopup.editor.dom.getClasses();
|
||||||
|
|
||||||
|
if (cl.length > 0) {
|
||||||
|
lst.options.length = 0;
|
||||||
|
lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('not_set'), '');
|
||||||
|
|
||||||
|
tinymce.each(cl, function(o) {
|
||||||
|
lst.options[lst.options.length] = new Option(o.title || o['class'], o['class']);
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
dom.remove(dom.getParent(id, 'tr'));
|
||||||
|
},
|
||||||
|
|
||||||
|
fillFileList : function(id, l) {
|
||||||
|
var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl;
|
||||||
|
|
||||||
|
l = window[l];
|
||||||
|
lst.options.length = 0;
|
||||||
|
|
||||||
|
if (l && l.length > 0) {
|
||||||
|
lst.options[lst.options.length] = new Option('', '');
|
||||||
|
|
||||||
|
tinymce.each(l, function(o) {
|
||||||
|
lst.options[lst.options.length] = new Option(o[0], o[1]);
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
dom.remove(dom.getParent(id, 'tr'));
|
||||||
|
},
|
||||||
|
|
||||||
|
resetImageData : function() {
|
||||||
|
var f = document.forms[0];
|
||||||
|
|
||||||
|
f.elements.width.value = f.elements.height.value = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
updateImageData : function(img, st) {
|
||||||
|
var f = document.forms[0];
|
||||||
|
|
||||||
|
if (!st) {
|
||||||
|
f.elements.width.value = img.width;
|
||||||
|
f.elements.height.value = img.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preloadImg = img;
|
||||||
|
},
|
||||||
|
|
||||||
|
changeAppearance : function() {
|
||||||
|
var ed = tinyMCEPopup.editor, f = document.forms[0], img = document.getElementById('alignSampleImg');
|
||||||
|
|
||||||
|
if (img) {
|
||||||
|
if (ed.getParam('inline_styles')) {
|
||||||
|
ed.dom.setAttrib(img, 'style', f.style.value);
|
||||||
|
} else {
|
||||||
|
img.align = f.align.value;
|
||||||
|
img.border = f.border.value;
|
||||||
|
img.hspace = f.hspace.value;
|
||||||
|
img.vspace = f.vspace.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
changeHeight : function() {
|
||||||
|
var f = document.forms[0], tp, t = this;
|
||||||
|
|
||||||
|
if (!f.constrain.checked || !t.preloadImg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.width.value == "" || f.height.value == "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
tp = (parseInt(f.width.value) / parseInt(t.preloadImg.width)) * t.preloadImg.height;
|
||||||
|
f.height.value = tp.toFixed(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
changeWidth : function() {
|
||||||
|
var f = document.forms[0], tp, t = this;
|
||||||
|
|
||||||
|
if (!f.constrain.checked || !t.preloadImg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.width.value == "" || f.height.value == "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
tp = (parseInt(f.height.value) / parseInt(t.preloadImg.height)) * t.preloadImg.width;
|
||||||
|
f.width.value = tp.toFixed(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStyle : function(ty) {
|
||||||
|
var dom = tinyMCEPopup.dom, st, v, f = document.forms[0], img = dom.create('img', {style : dom.get('style').value});
|
||||||
|
|
||||||
|
if (tinyMCEPopup.editor.settings.inline_styles) {
|
||||||
|
// Handle align
|
||||||
|
if (ty == 'align') {
|
||||||
|
dom.setStyle(img, 'float', '');
|
||||||
|
dom.setStyle(img, 'vertical-align', '');
|
||||||
|
|
||||||
|
v = getSelectValue(f, 'align');
|
||||||
|
if (v) {
|
||||||
|
if (v == 'left' || v == 'right')
|
||||||
|
dom.setStyle(img, 'float', v);
|
||||||
|
else
|
||||||
|
img.style.verticalAlign = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle border
|
||||||
|
if (ty == 'border') {
|
||||||
|
dom.setStyle(img, 'border', '');
|
||||||
|
|
||||||
|
v = f.border.value;
|
||||||
|
if (v || v == '0') {
|
||||||
|
if (v == '0')
|
||||||
|
img.style.border = '0';
|
||||||
|
else
|
||||||
|
img.style.border = v + 'px solid black';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle hspace
|
||||||
|
if (ty == 'hspace') {
|
||||||
|
dom.setStyle(img, 'marginLeft', '');
|
||||||
|
dom.setStyle(img, 'marginRight', '');
|
||||||
|
|
||||||
|
v = f.hspace.value;
|
||||||
|
if (v) {
|
||||||
|
img.style.marginLeft = v + 'px';
|
||||||
|
img.style.marginRight = v + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle vspace
|
||||||
|
if (ty == 'vspace') {
|
||||||
|
dom.setStyle(img, 'marginTop', '');
|
||||||
|
dom.setStyle(img, 'marginBottom', '');
|
||||||
|
|
||||||
|
v = f.vspace.value;
|
||||||
|
if (v) {
|
||||||
|
img.style.marginTop = v + 'px';
|
||||||
|
img.style.marginBottom = v + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge
|
||||||
|
dom.get('style').value = dom.serializeStyle(dom.parseStyle(img.style.cssText), 'img');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
changeMouseMove : function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
showPreviewImage : function(u, st) {
|
||||||
|
if (!u) {
|
||||||
|
tinyMCEPopup.dom.setHTML('prev', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!st && tinyMCEPopup.getParam("advimage_update_dimensions_onchange", true))
|
||||||
|
this.resetImageData();
|
||||||
|
|
||||||
|
u = tinyMCEPopup.editor.documentBaseURI.toAbsolute(u);
|
||||||
|
|
||||||
|
if (!st)
|
||||||
|
tinyMCEPopup.dom.setHTML('prev', '<img id="previewImg" src="' + u + '" border="0" onload="ImageDialog.updateImageData(this);" onerror="ImageDialog.resetImageData();" />');
|
||||||
|
else
|
||||||
|
tinyMCEPopup.dom.setHTML('prev', '<img id="previewImg" src="' + u + '" border="0" onload="ImageDialog.updateImageData(this, 1);" />');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageDialog.preInit();
|
||||||
|
tinyMCEPopup.onInit.add(ImageDialog.init, ImageDialog);
|
@ -0,0 +1,43 @@
|
|||||||
|
tinyMCE.addI18n('en.advimage_dlg',{
|
||||||
|
tab_general:"General",
|
||||||
|
tab_appearance:"Appearance",
|
||||||
|
tab_advanced:"Advanced",
|
||||||
|
general:"General",
|
||||||
|
title:"Title",
|
||||||
|
preview:"Preview",
|
||||||
|
constrain_proportions:"Constrain proportions",
|
||||||
|
langdir:"Language direction",
|
||||||
|
langcode:"Language code",
|
||||||
|
long_desc:"Long description link",
|
||||||
|
style:"Style",
|
||||||
|
classes:"Classes",
|
||||||
|
ltr:"Left to right",
|
||||||
|
rtl:"Right to left",
|
||||||
|
id:"Id",
|
||||||
|
map:"Image map",
|
||||||
|
swap_image:"Swap image",
|
||||||
|
alt_image:"Alternative image",
|
||||||
|
mouseover:"for mouse over",
|
||||||
|
mouseout:"for mouse out",
|
||||||
|
misc:"Miscellaneous",
|
||||||
|
example_img:"Appearance preview image",
|
||||||
|
missing_alt:"Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.",
|
||||||
|
dialog_title:"Insert/edit image",
|
||||||
|
src:"Image URL",
|
||||||
|
alt:"Image description",
|
||||||
|
list:"Image list",
|
||||||
|
border:"Border",
|
||||||
|
dimensions:"Dimensions",
|
||||||
|
vspace:"Vertical space",
|
||||||
|
hspace:"Horizontal space",
|
||||||
|
align:"Alignment",
|
||||||
|
align_baseline:"Baseline",
|
||||||
|
align_top:"Top",
|
||||||
|
align_middle:"Middle",
|
||||||
|
align_bottom:"Bottom",
|
||||||
|
align_texttop:"Text top",
|
||||||
|
align_textbottom:"Text bottom",
|
||||||
|
align_left:"Left",
|
||||||
|
align_right:"Right",
|
||||||
|
image_list:"Image list"
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
.mceLinkList, .mceAnchorList, #targetlist {width:280px;}
|
||||||
|
.mceActionPanel {margin-top:7px;}
|
||||||
|
.panel_wrapper div.current {height:320px;}
|
||||||
|
#classlist, #title, #href {width:280px;}
|
||||||
|
#popupurl, #popupname {width:200px;}
|
||||||
|
#popupwidth, #popupheight, #popupleft, #popuptop {width:30px;vertical-align:middle;text-align:center;}
|
||||||
|
#id, #style, #classes, #target, #dir, #hreflang, #lang, #charset, #type, #rel, #rev, #tabindex, #accesskey {width:200px;}
|
||||||
|
#events_panel input {width:200px;}
|
@ -0,0 +1 @@
|
|||||||
|
(function(){tinymce.create("tinymce.plugins.AdvancedLinkPlugin",{init:function(a,b){this.editor=a;a.addCommand("mceAdvLink",function(){var c=a.selection;if(c.isCollapsed()&&!a.dom.getParent(c.getNode(),"A")){return}a.windowManager.open({file:b+"/link.htm",width:480+parseInt(a.getLang("advlink.delta_width",0)),height:400+parseInt(a.getLang("advlink.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("link",{title:"advlink.link_desc",cmd:"mceAdvLink"});a.addShortcut("ctrl+k","advlink.advlink_desc","mceAdvLink");a.onNodeChange.add(function(d,c,f,e){c.setDisabled("link",e&&f.nodeName!="A");c.setActive("link",f.nodeName=="A"&&!f.name)})},getInfo:function(){return{longname:"Advanced link",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advlink",tinymce.plugins.AdvancedLinkPlugin)})();
|
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
tinymce.create('tinymce.plugins.AdvancedLinkPlugin', {
|
||||||
|
init : function(ed, url) {
|
||||||
|
this.editor = ed;
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
ed.addCommand('mceAdvLink', function() {
|
||||||
|
var se = ed.selection;
|
||||||
|
|
||||||
|
// No selection and not in link
|
||||||
|
if (se.isCollapsed() && !ed.dom.getParent(se.getNode(), 'A'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ed.windowManager.open({
|
||||||
|
file : url + '/link.htm',
|
||||||
|
width : 480 + parseInt(ed.getLang('advlink.delta_width', 0)),
|
||||||
|
height : 400 + parseInt(ed.getLang('advlink.delta_height', 0)),
|
||||||
|
inline : 1
|
||||||
|
}, {
|
||||||
|
plugin_url : url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register buttons
|
||||||
|
ed.addButton('link', {
|
||||||
|
title : 'advlink.link_desc',
|
||||||
|
cmd : 'mceAdvLink'
|
||||||
|
});
|
||||||
|
|
||||||
|
ed.addShortcut('ctrl+k', 'advlink.advlink_desc', 'mceAdvLink');
|
||||||
|
|
||||||
|
ed.onNodeChange.add(function(ed, cm, n, co) {
|
||||||
|
cm.setDisabled('link', co && n.nodeName != 'A');
|
||||||
|
cm.setActive('link', n.nodeName == 'A' && !n.name);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Advanced link',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
tinymce.PluginManager.add('advlink', tinymce.plugins.AdvancedLinkPlugin);
|
||||||
|
})();
|
@ -0,0 +1,528 @@
|
|||||||
|
/* Functions for the advlink plugin popup */
|
||||||
|
|
||||||
|
tinyMCEPopup.requireLangPack();
|
||||||
|
|
||||||
|
var templates = {
|
||||||
|
"window.open" : "window.open('${url}','${target}','${options}')"
|
||||||
|
};
|
||||||
|
|
||||||
|
function preinit() {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
if (url = tinyMCEPopup.getParam("external_link_list_url"))
|
||||||
|
document.write('<script language="javascript" type="text/javascript" src="' + tinyMCEPopup.editor.documentBaseURI.toAbsolute(url) + '"></script>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeClass() {
|
||||||
|
var f = document.forms[0];
|
||||||
|
|
||||||
|
f.classes.value = getSelectValue(f, 'classlist');
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
tinyMCEPopup.resizeToInnerSize();
|
||||||
|
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
var inst = tinyMCEPopup.editor;
|
||||||
|
var elm = inst.selection.getNode();
|
||||||
|
var action = "insert";
|
||||||
|
var html;
|
||||||
|
|
||||||
|
document.getElementById('hrefbrowsercontainer').innerHTML = getBrowserHTML('hrefbrowser','href','file','advlink');
|
||||||
|
document.getElementById('popupurlbrowsercontainer').innerHTML = getBrowserHTML('popupurlbrowser','popupurl','file','advlink');
|
||||||
|
document.getElementById('linklisthrefcontainer').innerHTML = getLinkListHTML('linklisthref','href');
|
||||||
|
document.getElementById('anchorlistcontainer').innerHTML = getAnchorListHTML('anchorlist','href');
|
||||||
|
document.getElementById('targetlistcontainer').innerHTML = getTargetListHTML('targetlist','target');
|
||||||
|
|
||||||
|
// Link list
|
||||||
|
html = getLinkListHTML('linklisthref','href');
|
||||||
|
if (html == "")
|
||||||
|
document.getElementById("linklisthrefrow").style.display = 'none';
|
||||||
|
else
|
||||||
|
document.getElementById("linklisthrefcontainer").innerHTML = html;
|
||||||
|
|
||||||
|
// Resize some elements
|
||||||
|
if (isVisible('hrefbrowser'))
|
||||||
|
document.getElementById('href').style.width = '260px';
|
||||||
|
|
||||||
|
if (isVisible('popupurlbrowser'))
|
||||||
|
document.getElementById('popupurl').style.width = '180px';
|
||||||
|
|
||||||
|
elm = inst.dom.getParent(elm, "A");
|
||||||
|
if (elm != null && elm.nodeName == "A")
|
||||||
|
action = "update";
|
||||||
|
|
||||||
|
formObj.insert.value = tinyMCEPopup.getLang(action, 'Insert', true);
|
||||||
|
|
||||||
|
setPopupControlsDisabled(true);
|
||||||
|
|
||||||
|
if (action == "update") {
|
||||||
|
var href = inst.dom.getAttrib(elm, 'href');
|
||||||
|
var onclick = inst.dom.getAttrib(elm, 'onclick');
|
||||||
|
|
||||||
|
// Setup form data
|
||||||
|
setFormValue('href', href);
|
||||||
|
setFormValue('title', inst.dom.getAttrib(elm, 'title'));
|
||||||
|
setFormValue('id', inst.dom.getAttrib(elm, 'id'));
|
||||||
|
setFormValue('style', inst.dom.getAttrib(elm, "style"));
|
||||||
|
setFormValue('rel', inst.dom.getAttrib(elm, 'rel'));
|
||||||
|
setFormValue('rev', inst.dom.getAttrib(elm, 'rev'));
|
||||||
|
setFormValue('charset', inst.dom.getAttrib(elm, 'charset'));
|
||||||
|
setFormValue('hreflang', inst.dom.getAttrib(elm, 'hreflang'));
|
||||||
|
setFormValue('dir', inst.dom.getAttrib(elm, 'dir'));
|
||||||
|
setFormValue('lang', inst.dom.getAttrib(elm, 'lang'));
|
||||||
|
setFormValue('tabindex', inst.dom.getAttrib(elm, 'tabindex', typeof(elm.tabindex) != "undefined" ? elm.tabindex : ""));
|
||||||
|
setFormValue('accesskey', inst.dom.getAttrib(elm, 'accesskey', typeof(elm.accesskey) != "undefined" ? elm.accesskey : ""));
|
||||||
|
setFormValue('type', inst.dom.getAttrib(elm, 'type'));
|
||||||
|
setFormValue('onfocus', inst.dom.getAttrib(elm, 'onfocus'));
|
||||||
|
setFormValue('onblur', inst.dom.getAttrib(elm, 'onblur'));
|
||||||
|
setFormValue('onclick', onclick);
|
||||||
|
setFormValue('ondblclick', inst.dom.getAttrib(elm, 'ondblclick'));
|
||||||
|
setFormValue('onmousedown', inst.dom.getAttrib(elm, 'onmousedown'));
|
||||||
|
setFormValue('onmouseup', inst.dom.getAttrib(elm, 'onmouseup'));
|
||||||
|
setFormValue('onmouseover', inst.dom.getAttrib(elm, 'onmouseover'));
|
||||||
|
setFormValue('onmousemove', inst.dom.getAttrib(elm, 'onmousemove'));
|
||||||
|
setFormValue('onmouseout', inst.dom.getAttrib(elm, 'onmouseout'));
|
||||||
|
setFormValue('onkeypress', inst.dom.getAttrib(elm, 'onkeypress'));
|
||||||
|
setFormValue('onkeydown', inst.dom.getAttrib(elm, 'onkeydown'));
|
||||||
|
setFormValue('onkeyup', inst.dom.getAttrib(elm, 'onkeyup'));
|
||||||
|
setFormValue('target', inst.dom.getAttrib(elm, 'target'));
|
||||||
|
setFormValue('classes', inst.dom.getAttrib(elm, 'class'));
|
||||||
|
|
||||||
|
// Parse onclick data
|
||||||
|
if (onclick != null && onclick.indexOf('window.open') != -1)
|
||||||
|
parseWindowOpen(onclick);
|
||||||
|
else
|
||||||
|
parseFunction(onclick);
|
||||||
|
|
||||||
|
// Select by the values
|
||||||
|
selectByValue(formObj, 'dir', inst.dom.getAttrib(elm, 'dir'));
|
||||||
|
selectByValue(formObj, 'rel', inst.dom.getAttrib(elm, 'rel'));
|
||||||
|
selectByValue(formObj, 'rev', inst.dom.getAttrib(elm, 'rev'));
|
||||||
|
selectByValue(formObj, 'linklisthref', href);
|
||||||
|
|
||||||
|
if (href.charAt(0) == '#')
|
||||||
|
selectByValue(formObj, 'anchorlist', href);
|
||||||
|
|
||||||
|
addClassesToList('classlist', 'advlink_styles');
|
||||||
|
|
||||||
|
selectByValue(formObj, 'classlist', inst.dom.getAttrib(elm, 'class'), true);
|
||||||
|
selectByValue(formObj, 'targetlist', inst.dom.getAttrib(elm, 'target'), true);
|
||||||
|
} else
|
||||||
|
addClassesToList('classlist', 'advlink_styles');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPrefix(n) {
|
||||||
|
if (n.value && Validator.isEmail(n) && !/^\s*mailto:/i.test(n.value) && confirm(tinyMCEPopup.getLang('advlink_dlg.is_email')))
|
||||||
|
n.value = 'mailto:' + n.value;
|
||||||
|
|
||||||
|
if (/^\s*www\./i.test(n.value) && confirm(tinyMCEPopup.getLang('advlink_dlg.is_external')))
|
||||||
|
n.value = 'http://' + n.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFormValue(name, value) {
|
||||||
|
document.forms[0].elements[name].value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseWindowOpen(onclick) {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
|
||||||
|
// Preprocess center code
|
||||||
|
if (onclick.indexOf('return false;') != -1) {
|
||||||
|
formObj.popupreturn.checked = true;
|
||||||
|
onclick = onclick.replace('return false;', '');
|
||||||
|
} else
|
||||||
|
formObj.popupreturn.checked = false;
|
||||||
|
|
||||||
|
var onClickData = parseLink(onclick);
|
||||||
|
|
||||||
|
if (onClickData != null) {
|
||||||
|
formObj.ispopup.checked = true;
|
||||||
|
setPopupControlsDisabled(false);
|
||||||
|
|
||||||
|
var onClickWindowOptions = parseOptions(onClickData['options']);
|
||||||
|
var url = onClickData['url'];
|
||||||
|
|
||||||
|
formObj.popupname.value = onClickData['target'];
|
||||||
|
formObj.popupurl.value = url;
|
||||||
|
formObj.popupwidth.value = getOption(onClickWindowOptions, 'width');
|
||||||
|
formObj.popupheight.value = getOption(onClickWindowOptions, 'height');
|
||||||
|
|
||||||
|
formObj.popupleft.value = getOption(onClickWindowOptions, 'left');
|
||||||
|
formObj.popuptop.value = getOption(onClickWindowOptions, 'top');
|
||||||
|
|
||||||
|
if (formObj.popupleft.value.indexOf('screen') != -1)
|
||||||
|
formObj.popupleft.value = "c";
|
||||||
|
|
||||||
|
if (formObj.popuptop.value.indexOf('screen') != -1)
|
||||||
|
formObj.popuptop.value = "c";
|
||||||
|
|
||||||
|
formObj.popuplocation.checked = getOption(onClickWindowOptions, 'location') == "yes";
|
||||||
|
formObj.popupscrollbars.checked = getOption(onClickWindowOptions, 'scrollbars') == "yes";
|
||||||
|
formObj.popupmenubar.checked = getOption(onClickWindowOptions, 'menubar') == "yes";
|
||||||
|
formObj.popupresizable.checked = getOption(onClickWindowOptions, 'resizable') == "yes";
|
||||||
|
formObj.popuptoolbar.checked = getOption(onClickWindowOptions, 'toolbar') == "yes";
|
||||||
|
formObj.popupstatus.checked = getOption(onClickWindowOptions, 'status') == "yes";
|
||||||
|
formObj.popupdependent.checked = getOption(onClickWindowOptions, 'dependent') == "yes";
|
||||||
|
|
||||||
|
buildOnClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFunction(onclick) {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
var onClickData = parseLink(onclick);
|
||||||
|
|
||||||
|
// TODO: Add stuff here
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOption(opts, name) {
|
||||||
|
return typeof(opts[name]) == "undefined" ? "" : opts[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopupControlsDisabled(state) {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
|
||||||
|
formObj.popupname.disabled = state;
|
||||||
|
formObj.popupurl.disabled = state;
|
||||||
|
formObj.popupwidth.disabled = state;
|
||||||
|
formObj.popupheight.disabled = state;
|
||||||
|
formObj.popupleft.disabled = state;
|
||||||
|
formObj.popuptop.disabled = state;
|
||||||
|
formObj.popuplocation.disabled = state;
|
||||||
|
formObj.popupscrollbars.disabled = state;
|
||||||
|
formObj.popupmenubar.disabled = state;
|
||||||
|
formObj.popupresizable.disabled = state;
|
||||||
|
formObj.popuptoolbar.disabled = state;
|
||||||
|
formObj.popupstatus.disabled = state;
|
||||||
|
formObj.popupreturn.disabled = state;
|
||||||
|
formObj.popupdependent.disabled = state;
|
||||||
|
|
||||||
|
setBrowserDisabled('popupurlbrowser', state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLink(link) {
|
||||||
|
link = link.replace(new RegExp(''', 'g'), "'");
|
||||||
|
|
||||||
|
var fnName = link.replace(new RegExp("\\s*([A-Za-z0-9\.]*)\\s*\\(.*", "gi"), "$1");
|
||||||
|
|
||||||
|
// Is function name a template function
|
||||||
|
var template = templates[fnName];
|
||||||
|
if (template) {
|
||||||
|
// Build regexp
|
||||||
|
var variableNames = template.match(new RegExp("'?\\$\\{[A-Za-z0-9\.]*\\}'?", "gi"));
|
||||||
|
var regExp = "\\s*[A-Za-z0-9\.]*\\s*\\(";
|
||||||
|
var replaceStr = "";
|
||||||
|
for (var i=0; i<variableNames.length; i++) {
|
||||||
|
// Is string value
|
||||||
|
if (variableNames[i].indexOf("'${") != -1)
|
||||||
|
regExp += "'(.*)'";
|
||||||
|
else // Number value
|
||||||
|
regExp += "([0-9]*)";
|
||||||
|
|
||||||
|
replaceStr += "$" + (i+1);
|
||||||
|
|
||||||
|
// Cleanup variable name
|
||||||
|
variableNames[i] = variableNames[i].replace(new RegExp("[^A-Za-z0-9]", "gi"), "");
|
||||||
|
|
||||||
|
if (i != variableNames.length-1) {
|
||||||
|
regExp += "\\s*,\\s*";
|
||||||
|
replaceStr += "<delim>";
|
||||||
|
} else
|
||||||
|
regExp += ".*";
|
||||||
|
}
|
||||||
|
|
||||||
|
regExp += "\\);?";
|
||||||
|
|
||||||
|
// Build variable array
|
||||||
|
var variables = [];
|
||||||
|
variables["_function"] = fnName;
|
||||||
|
var variableValues = link.replace(new RegExp(regExp, "gi"), replaceStr).split('<delim>');
|
||||||
|
for (var i=0; i<variableNames.length; i++)
|
||||||
|
variables[variableNames[i]] = variableValues[i];
|
||||||
|
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOptions(opts) {
|
||||||
|
if (opts == null || opts == "")
|
||||||
|
return [];
|
||||||
|
|
||||||
|
// Cleanup the options
|
||||||
|
opts = opts.toLowerCase();
|
||||||
|
opts = opts.replace(/;/g, ",");
|
||||||
|
opts = opts.replace(/[^0-9a-z=,]/g, "");
|
||||||
|
|
||||||
|
var optionChunks = opts.split(',');
|
||||||
|
var options = [];
|
||||||
|
|
||||||
|
for (var i=0; i<optionChunks.length; i++) {
|
||||||
|
var parts = optionChunks[i].split('=');
|
||||||
|
|
||||||
|
if (parts.length == 2)
|
||||||
|
options[parts[0]] = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOnClick() {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
|
||||||
|
if (!formObj.ispopup.checked) {
|
||||||
|
formObj.onclick.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onclick = "window.open('";
|
||||||
|
var url = formObj.popupurl.value;
|
||||||
|
|
||||||
|
onclick += url + "','";
|
||||||
|
onclick += formObj.popupname.value + "','";
|
||||||
|
|
||||||
|
if (formObj.popuplocation.checked)
|
||||||
|
onclick += "location=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupscrollbars.checked)
|
||||||
|
onclick += "scrollbars=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupmenubar.checked)
|
||||||
|
onclick += "menubar=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupresizable.checked)
|
||||||
|
onclick += "resizable=yes,";
|
||||||
|
|
||||||
|
if (formObj.popuptoolbar.checked)
|
||||||
|
onclick += "toolbar=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupstatus.checked)
|
||||||
|
onclick += "status=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupdependent.checked)
|
||||||
|
onclick += "dependent=yes,";
|
||||||
|
|
||||||
|
if (formObj.popupwidth.value != "")
|
||||||
|
onclick += "width=" + formObj.popupwidth.value + ",";
|
||||||
|
|
||||||
|
if (formObj.popupheight.value != "")
|
||||||
|
onclick += "height=" + formObj.popupheight.value + ",";
|
||||||
|
|
||||||
|
if (formObj.popupleft.value != "") {
|
||||||
|
if (formObj.popupleft.value != "c")
|
||||||
|
onclick += "left=" + formObj.popupleft.value + ",";
|
||||||
|
else
|
||||||
|
onclick += "left='+(screen.availWidth/2-" + (formObj.popupwidth.value/2) + ")+',";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formObj.popuptop.value != "") {
|
||||||
|
if (formObj.popuptop.value != "c")
|
||||||
|
onclick += "top=" + formObj.popuptop.value + ",";
|
||||||
|
else
|
||||||
|
onclick += "top='+(screen.availHeight/2-" + (formObj.popupheight.value/2) + ")+',";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onclick.charAt(onclick.length-1) == ',')
|
||||||
|
onclick = onclick.substring(0, onclick.length-1);
|
||||||
|
|
||||||
|
onclick += "');";
|
||||||
|
|
||||||
|
if (formObj.popupreturn.checked)
|
||||||
|
onclick += "return false;";
|
||||||
|
|
||||||
|
// tinyMCE.debug(onclick);
|
||||||
|
|
||||||
|
formObj.onclick.value = onclick;
|
||||||
|
|
||||||
|
if (formObj.href.value == "")
|
||||||
|
formObj.href.value = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAttrib(elm, attrib, value) {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
var valueElm = formObj.elements[attrib.toLowerCase()];
|
||||||
|
var dom = tinyMCEPopup.editor.dom;
|
||||||
|
|
||||||
|
if (typeof(value) == "undefined" || value == null) {
|
||||||
|
value = "";
|
||||||
|
|
||||||
|
if (valueElm)
|
||||||
|
value = valueElm.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the style
|
||||||
|
if (attrib == 'style')
|
||||||
|
value = dom.serializeStyle(dom.parseStyle(value), 'a');
|
||||||
|
|
||||||
|
dom.setAttrib(elm, attrib, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnchorListHTML(id, target) {
|
||||||
|
var inst = tinyMCEPopup.editor;
|
||||||
|
var nodes = inst.dom.select('a.mceItemAnchor,img.mceItemAnchor'), name, i;
|
||||||
|
var html = "";
|
||||||
|
|
||||||
|
html += '<select id="' + id + '" name="' + id + '" class="mceAnchorList" o2nfocus="tinyMCE.addSelectAccessibility(event, this, window);" onchange="this.form.' + target + '.value=';
|
||||||
|
html += 'this.options[this.selectedIndex].value;">';
|
||||||
|
html += '<option value="">---</option>';
|
||||||
|
|
||||||
|
for (i=0; i<nodes.length; i++) {
|
||||||
|
if ((name = inst.dom.getAttrib(nodes[i], "name")) != "")
|
||||||
|
html += '<option value="#' + name + '">' + name + '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertAction() {
|
||||||
|
var inst = tinyMCEPopup.editor;
|
||||||
|
var elm, elementArray, i;
|
||||||
|
|
||||||
|
elm = inst.selection.getNode();
|
||||||
|
checkPrefix(document.forms[0].href);
|
||||||
|
|
||||||
|
elm = inst.dom.getParent(elm, "A");
|
||||||
|
|
||||||
|
// Remove element if there is no href
|
||||||
|
if (!document.forms[0].href.value) {
|
||||||
|
tinyMCEPopup.execCommand("mceBeginUndoLevel");
|
||||||
|
i = inst.selection.getBookmark();
|
||||||
|
inst.dom.remove(elm, 1);
|
||||||
|
inst.selection.moveToBookmark(i);
|
||||||
|
tinyMCEPopup.execCommand("mceEndUndoLevel");
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyMCEPopup.execCommand("mceBeginUndoLevel");
|
||||||
|
|
||||||
|
// Create new anchor elements
|
||||||
|
if (elm == null) {
|
||||||
|
inst.getDoc().execCommand("unlink", false, null);
|
||||||
|
tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1});
|
||||||
|
|
||||||
|
elementArray = tinymce.grep(inst.dom.select("a"), function(n) {return inst.dom.getAttrib(n, 'href') == '#mce_temp_url#';});
|
||||||
|
for (i=0; i<elementArray.length; i++)
|
||||||
|
setAllAttribs(elm = elementArray[i]);
|
||||||
|
} else
|
||||||
|
setAllAttribs(elm);
|
||||||
|
|
||||||
|
// Don't move caret if selection was image
|
||||||
|
if (elm.childNodes.length != 1 || elm.firstChild.nodeName != 'IMG') {
|
||||||
|
inst.focus();
|
||||||
|
inst.selection.select(elm);
|
||||||
|
inst.selection.collapse(0);
|
||||||
|
tinyMCEPopup.storeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyMCEPopup.execCommand("mceEndUndoLevel");
|
||||||
|
tinyMCEPopup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAllAttribs(elm) {
|
||||||
|
var formObj = document.forms[0];
|
||||||
|
var href = formObj.href.value;
|
||||||
|
var target = getSelectValue(formObj, 'targetlist');
|
||||||
|
|
||||||
|
setAttrib(elm, 'href', href);
|
||||||
|
setAttrib(elm, 'title');
|
||||||
|
setAttrib(elm, 'target', target == '_self' ? '' : target);
|
||||||
|
setAttrib(elm, 'id');
|
||||||
|
setAttrib(elm, 'style');
|
||||||
|
setAttrib(elm, 'class', getSelectValue(formObj, 'classlist'));
|
||||||
|
setAttrib(elm, 'rel');
|
||||||
|
setAttrib(elm, 'rev');
|
||||||
|
setAttrib(elm, 'charset');
|
||||||
|
setAttrib(elm, 'hreflang');
|
||||||
|
setAttrib(elm, 'dir');
|
||||||
|
setAttrib(elm, 'lang');
|
||||||
|
setAttrib(elm, 'tabindex');
|
||||||
|
setAttrib(elm, 'accesskey');
|
||||||
|
setAttrib(elm, 'type');
|
||||||
|
setAttrib(elm, 'onfocus');
|
||||||
|
setAttrib(elm, 'onblur');
|
||||||
|
setAttrib(elm, 'onclick');
|
||||||
|
setAttrib(elm, 'ondblclick');
|
||||||
|
setAttrib(elm, 'onmousedown');
|
||||||
|
setAttrib(elm, 'onmouseup');
|
||||||
|
setAttrib(elm, 'onmouseover');
|
||||||
|
setAttrib(elm, 'onmousemove');
|
||||||
|
setAttrib(elm, 'onmouseout');
|
||||||
|
setAttrib(elm, 'onkeypress');
|
||||||
|
setAttrib(elm, 'onkeydown');
|
||||||
|
setAttrib(elm, 'onkeyup');
|
||||||
|
|
||||||
|
// Refresh in old MSIE
|
||||||
|
if (tinyMCE.isMSIE5)
|
||||||
|
elm.outerHTML = elm.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectValue(form_obj, field_name) {
|
||||||
|
var elm = form_obj.elements[field_name];
|
||||||
|
|
||||||
|
if (!elm || elm.options == null || elm.selectedIndex == -1)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return elm.options[elm.selectedIndex].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinkListHTML(elm_id, target_form_element, onchange_func) {
|
||||||
|
if (typeof(tinyMCELinkList) == "undefined" || tinyMCELinkList.length == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var html = "";
|
||||||
|
|
||||||
|
html += '<select id="' + elm_id + '" name="' + elm_id + '"';
|
||||||
|
html += ' class="mceLinkList" onfoc2us="tinyMCE.addSelectAccessibility(event, this, window);" onchange="this.form.' + target_form_element + '.value=';
|
||||||
|
html += 'this.options[this.selectedIndex].value;';
|
||||||
|
|
||||||
|
if (typeof(onchange_func) != "undefined")
|
||||||
|
html += onchange_func + '(\'' + target_form_element + '\',this.options[this.selectedIndex].text,this.options[this.selectedIndex].value);';
|
||||||
|
|
||||||
|
html += '"><option value="">---</option>';
|
||||||
|
|
||||||
|
for (var i=0; i<tinyMCELinkList.length; i++)
|
||||||
|
html += '<option value="' + tinyMCELinkList[i][1] + '">' + tinyMCELinkList[i][0] + '</option>';
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
|
||||||
|
return html;
|
||||||
|
|
||||||
|
// tinyMCE.debug('-- image list start --', html, '-- image list end --');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTargetListHTML(elm_id, target_form_element) {
|
||||||
|
var targets = tinyMCEPopup.getParam('theme_advanced_link_targets', '').split(';');
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
html += '<select id="' + elm_id + '" name="' + elm_id + '" onf2ocus="tinyMCE.addSelectAccessibility(event, this, window);" onchange="this.form.' + target_form_element + '.value=';
|
||||||
|
html += 'this.options[this.selectedIndex].value;">';
|
||||||
|
html += '<option value="_self">' + tinyMCEPopup.getLang('advlink_dlg.target_same') + '</option>';
|
||||||
|
html += '<option value="_blank">' + tinyMCEPopup.getLang('advlink_dlg.target_blank') + ' (_blank)</option>';
|
||||||
|
html += '<option value="_parent">' + tinyMCEPopup.getLang('advlink_dlg.target_parent') + ' (_parent)</option>';
|
||||||
|
html += '<option value="_top">' + tinyMCEPopup.getLang('advlink_dlg.target_top') + ' (_top)</option>';
|
||||||
|
|
||||||
|
for (var i=0; i<targets.length; i++) {
|
||||||
|
var key, value;
|
||||||
|
|
||||||
|
if (targets[i] == "")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
key = targets[i].split('=')[0];
|
||||||
|
value = targets[i].split('=')[1];
|
||||||
|
|
||||||
|
html += '<option value="' + key + '">' + value + ' (' + key + ')</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</select>';
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While loading
|
||||||
|
preinit();
|
||||||
|
tinyMCEPopup.onInit.add(init);
|
@ -0,0 +1,52 @@
|
|||||||
|
tinyMCE.addI18n('en.advlink_dlg',{
|
||||||
|
title:"Insert/edit link",
|
||||||
|
url:"Link URL",
|
||||||
|
target:"Target",
|
||||||
|
titlefield:"Title",
|
||||||
|
is_email:"The URL you entered seems to be an email address, do you want to add the required mailto: prefix?",
|
||||||
|
is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?",
|
||||||
|
list:"Link list",
|
||||||
|
general_tab:"General",
|
||||||
|
popup_tab:"Popup",
|
||||||
|
events_tab:"Events",
|
||||||
|
advanced_tab:"Advanced",
|
||||||
|
general_props:"General properties",
|
||||||
|
popup_props:"Popup properties",
|
||||||
|
event_props:"Events",
|
||||||
|
advanced_props:"Advanced properties",
|
||||||
|
popup_opts:"Options",
|
||||||
|
anchor_names:"Anchors",
|
||||||
|
target_same:"Open in this window / frame",
|
||||||
|
target_parent:"Open in parent window / frame",
|
||||||
|
target_top:"Open in top frame (replaces all frames)",
|
||||||
|
target_blank:"Open in new window",
|
||||||
|
popup:"Javascript popup",
|
||||||
|
popup_url:"Popup URL",
|
||||||
|
popup_name:"Window name",
|
||||||
|
popup_return:"Insert 'return false'",
|
||||||
|
popup_scrollbars:"Show scrollbars",
|
||||||
|
popup_statusbar:"Show status bar",
|
||||||
|
popup_toolbar:"Show toolbars",
|
||||||
|
popup_menubar:"Show menu bar",
|
||||||
|
popup_location:"Show location bar",
|
||||||
|
popup_resizable:"Make window resizable",
|
||||||
|
popup_dependent:"Dependent (Mozilla/Firefox only)",
|
||||||
|
popup_size:"Size",
|
||||||
|
popup_position:"Position (X/Y)",
|
||||||
|
id:"Id",
|
||||||
|
style:"Style",
|
||||||
|
classes:"Classes",
|
||||||
|
target_name:"Target name",
|
||||||
|
langdir:"Language direction",
|
||||||
|
target_langcode:"Target language",
|
||||||
|
langcode:"Language code",
|
||||||
|
encoding:"Target character encoding",
|
||||||
|
mime:"Target MIME type",
|
||||||
|
rel:"Relationship page to target",
|
||||||
|
rev:"Relationship target to page",
|
||||||
|
tabindex:"Tabindex",
|
||||||
|
accesskey:"Accesskey",
|
||||||
|
ltr:"Left to right",
|
||||||
|
rtl:"Right to left",
|
||||||
|
link_list:"Link list"
|
||||||
|
});
|
338
scribeengine/public/scripts/tinymce/plugins/advlink/link.htm
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{#advlink_dlg.title}</title>
|
||||||
|
<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/mctabs.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/form_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../../utils/validate.js"></script>
|
||||||
|
<script type="text/javascript" src="js/advlink.js"></script>
|
||||||
|
<link href="css/advlink.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body id="advlink" style="display: none">
|
||||||
|
<form onsubmit="insertAction();return false;" action="#">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advlink_dlg.general_tab}</a></span></li>
|
||||||
|
<li id="popup_tab"><span><a href="javascript:mcTabs.displayTab('popup_tab','popup_panel');" onmousedown="return false;">{#advlink_dlg.popup_tab}</a></span></li>
|
||||||
|
<li id="events_tab"><span><a href="javascript:mcTabs.displayTab('events_tab','events_panel');" onmousedown="return false;">{#advlink_dlg.events_tab}</a></span></li>
|
||||||
|
<li id="advanced_tab"><span><a href="javascript:mcTabs.displayTab('advanced_tab','advanced_panel');" onmousedown="return false;">{#advlink_dlg.advanced_tab}</a></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel_wrapper">
|
||||||
|
<div id="general_panel" class="panel current">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advlink_dlg.general_props}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="4" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap"><label id="hreflabel" for="href">{#advlink_dlg.url}</label></td>
|
||||||
|
<td><table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input id="href" name="href" type="text" class="mceFocus" value="" onchange="selectByValue(this.form,'linklisthref',this.value);" /></td>
|
||||||
|
<td id="hrefbrowsercontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr id="linklisthrefrow">
|
||||||
|
<td class="column1"><label for="linklisthref">{#advlink_dlg.list}</label></td>
|
||||||
|
<td colspan="2" id="linklisthrefcontainer"><select id="linklisthref"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="anchorlist">{#advlink_dlg.anchor_names}</label></td>
|
||||||
|
<td colspan="2" id="anchorlistcontainer"><select id="anchorlist"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label id="targetlistlabel" for="targetlist">{#advlink_dlg.target}</label></td>
|
||||||
|
<td id="targetlistcontainer"><select id="targetlist"><option value=""></option></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap"><label id="titlelabel" for="title">{#advlink_dlg.titlefield}</label></td>
|
||||||
|
<td><input id="title" name="title" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label id="classlabel" for="classlist">{#class_name}</label></td>
|
||||||
|
<td>
|
||||||
|
<select id="classlist" name="classlist" onchange="changeClass();">
|
||||||
|
<option value="" selected="selected">{#not_set}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="popup_panel" class="panel">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advlink_dlg.popup_props}</legend>
|
||||||
|
|
||||||
|
<input type="checkbox" id="ispopup" name="ispopup" class="radio" onclick="setPopupControlsDisabled(!this.checked);buildOnClick();" />
|
||||||
|
<label id="ispopuplabel" for="ispopup">{#advlink_dlg.popup}</label>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="0" cellspacing="4">
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap"><label for="popupurl">{#advlink_dlg.popup_url}</label> </td>
|
||||||
|
<td>
|
||||||
|
<table border="0" cellspacing="0" cellpadding="0">
|
||||||
|
<tr>
|
||||||
|
<td><input type="text" name="popupurl" id="popupurl" value="" onchange="buildOnClick();" /></td>
|
||||||
|
<td id="popupurlbrowsercontainer"> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap"><label for="popupname">{#advlink_dlg.popup_name}</label> </td>
|
||||||
|
<td><input type="text" name="popupname" id="popupname" value="" onchange="buildOnClick();" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap"><label>{#advlink_dlg.popup_size}</label> </td>
|
||||||
|
<td class="nowrap">
|
||||||
|
<input type="text" id="popupwidth" name="popupwidth" value="" onchange="buildOnClick();" /> x
|
||||||
|
<input type="text" id="popupheight" name="popupheight" value="" onchange="buildOnClick();" /> px
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="nowrap" id="labelleft"><label>{#advlink_dlg.popup_position}</label> </td>
|
||||||
|
<td class="nowrap">
|
||||||
|
<input type="text" id="popupleft" name="popupleft" value="" onchange="buildOnClick();" /> /
|
||||||
|
<input type="text" id="popuptop" name="popuptop" value="" onchange="buildOnClick();" /> (c /c = center)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advlink_dlg.popup_opts}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="0" cellspacing="4">
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" id="popuplocation" name="popuplocation" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popuplocationlabel" for="popuplocation">{#advlink_dlg.popup_location}</label></td>
|
||||||
|
<td><input type="checkbox" id="popupscrollbars" name="popupscrollbars" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popupscrollbarslabel" for="popupscrollbars">{#advlink_dlg.popup_scrollbars}</label></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" id="popupmenubar" name="popupmenubar" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popupmenubarlabel" for="popupmenubar">{#advlink_dlg.popup_menubar}</label></td>
|
||||||
|
<td><input type="checkbox" id="popupresizable" name="popupresizable" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popupresizablelabel" for="popupresizable">{#advlink_dlg.popup_resizable}</label></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" id="popuptoolbar" name="popuptoolbar" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popuptoolbarlabel" for="popuptoolbar">{#advlink_dlg.popup_toolbar}</label></td>
|
||||||
|
<td><input type="checkbox" id="popupdependent" name="popupdependent" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popupdependentlabel" for="popupdependent">{#advlink_dlg.popup_dependent}</label></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" id="popupstatus" name="popupstatus" class="checkbox" onchange="buildOnClick();" /></td>
|
||||||
|
<td class="nowrap"><label id="popupstatuslabel" for="popupstatus">{#advlink_dlg.popup_statusbar}</label></td>
|
||||||
|
<td><input type="checkbox" id="popupreturn" name="popupreturn" class="checkbox" onchange="buildOnClick();" checked="checked" /></td>
|
||||||
|
<td class="nowrap"><label id="popupreturnlabel" for="popupreturn">{#advlink_dlg.popup_return}</label></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="advanced_panel" class="panel">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advlink_dlg.advanced_props}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="0" cellspacing="4">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="idlabel" for="id">{#advlink_dlg.id}</label></td>
|
||||||
|
<td><input id="id" name="id" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="stylelabel" for="style">{#advlink_dlg.style}</label></td>
|
||||||
|
<td><input type="text" id="style" name="style" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="classeslabel" for="classes">{#advlink_dlg.classes}</label></td>
|
||||||
|
<td><input type="text" id="classes" name="classes" value="" onchange="selectByValue(this.form,'classlist',this.value,true);" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="targetlabel" for="target">{#advlink_dlg.target_name}</label></td>
|
||||||
|
<td><input type="text" id="target" name="target" value="" onchange="selectByValue(this.form,'targetlist',this.value,true);" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="dirlabel" for="dir">{#advlink_dlg.langdir}</label></td>
|
||||||
|
<td>
|
||||||
|
<select id="dir" name="dir">
|
||||||
|
<option value="">{#not_set}</option>
|
||||||
|
<option value="ltr">{#advlink_dlg.ltr}</option>
|
||||||
|
<option value="rtl">{#advlink_dlg.rtl}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="hreflanglabel" for="hreflang">{#advlink_dlg.target_langcode}</label></td>
|
||||||
|
<td><input type="text" id="hreflang" name="hreflang" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label id="langlabel" for="lang">{#advlink_dlg.langcode}</label></td>
|
||||||
|
<td>
|
||||||
|
<input id="lang" name="lang" type="text" value="" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="charsetlabel" for="charset">{#advlink_dlg.encoding}</label></td>
|
||||||
|
<td><input type="text" id="charset" name="charset" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="typelabel" for="type">{#advlink_dlg.mime}</label></td>
|
||||||
|
<td><input type="text" id="type" name="type" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="rellabel" for="rel">{#advlink_dlg.rel}</label></td>
|
||||||
|
<td><select id="rel" name="rel">
|
||||||
|
<option value="">{#not_set}</option>
|
||||||
|
<option value="lightbox">Lightbox</option>
|
||||||
|
<option value="alternate">Alternate</option>
|
||||||
|
<option value="designates">Designates</option>
|
||||||
|
<option value="stylesheet">Stylesheet</option>
|
||||||
|
<option value="start">Start</option>
|
||||||
|
<option value="next">Next</option>
|
||||||
|
<option value="prev">Prev</option>
|
||||||
|
<option value="contents">Contents</option>
|
||||||
|
<option value="index">Index</option>
|
||||||
|
<option value="glossary">Glossary</option>
|
||||||
|
<option value="copyright">Copyright</option>
|
||||||
|
<option value="chapter">Chapter</option>
|
||||||
|
<option value="subsection">Subsection</option>
|
||||||
|
<option value="appendix">Appendix</option>
|
||||||
|
<option value="help">Help</option>
|
||||||
|
<option value="bookmark">Bookmark</option>
|
||||||
|
<option value="nofollow">No Follow</option>
|
||||||
|
<option value="tag">Tag</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="revlabel" for="rev">{#advlink_dlg.rev}</label></td>
|
||||||
|
<td><select id="rev" name="rev">
|
||||||
|
<option value="">{#not_set}</option>
|
||||||
|
<option value="alternate">Alternate</option>
|
||||||
|
<option value="designates">Designates</option>
|
||||||
|
<option value="stylesheet">Stylesheet</option>
|
||||||
|
<option value="start">Start</option>
|
||||||
|
<option value="next">Next</option>
|
||||||
|
<option value="prev">Prev</option>
|
||||||
|
<option value="contents">Contents</option>
|
||||||
|
<option value="index">Index</option>
|
||||||
|
<option value="glossary">Glossary</option>
|
||||||
|
<option value="copyright">Copyright</option>
|
||||||
|
<option value="chapter">Chapter</option>
|
||||||
|
<option value="subsection">Subsection</option>
|
||||||
|
<option value="appendix">Appendix</option>
|
||||||
|
<option value="help">Help</option>
|
||||||
|
<option value="bookmark">Bookmark</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="tabindexlabel" for="tabindex">{#advlink_dlg.tabindex}</label></td>
|
||||||
|
<td><input type="text" id="tabindex" name="tabindex" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><label id="accesskeylabel" for="accesskey">{#advlink_dlg.accesskey}</label></td>
|
||||||
|
<td><input type="text" id="accesskey" name="accesskey" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="events_panel" class="panel">
|
||||||
|
<fieldset>
|
||||||
|
<legend>{#advlink_dlg.event_props}</legend>
|
||||||
|
|
||||||
|
<table border="0" cellpadding="0" cellspacing="4">
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onfocus">onfocus</label></td>
|
||||||
|
<td><input id="onfocus" name="onfocus" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onblur">onblur</label></td>
|
||||||
|
<td><input id="onblur" name="onblur" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onclick">onclick</label></td>
|
||||||
|
<td><input id="onclick" name="onclick" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="ondblclick">ondblclick</label></td>
|
||||||
|
<td><input id="ondblclick" name="ondblclick" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onmousedown">onmousedown</label></td>
|
||||||
|
<td><input id="onmousedown" name="onmousedown" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onmouseup">onmouseup</label></td>
|
||||||
|
<td><input id="onmouseup" name="onmouseup" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onmouseover">onmouseover</label></td>
|
||||||
|
<td><input id="onmouseover" name="onmouseover" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onmousemove">onmousemove</label></td>
|
||||||
|
<td><input id="onmousemove" name="onmousemove" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onmouseout">onmouseout</label></td>
|
||||||
|
<td><input id="onmouseout" name="onmouseout" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onkeypress">onkeypress</label></td>
|
||||||
|
<td><input id="onkeypress" name="onkeypress" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onkeydown">onkeydown</label></td>
|
||||||
|
<td><input id="onkeydown" name="onkeydown" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column1"><label for="onkeyup">onkeyup</label></td>
|
||||||
|
<td><input id="onkeyup" name="onkeyup" type="text" value="" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mceActionPanel">
|
||||||
|
<div style="float: left">
|
||||||
|
<input type="submit" id="insert" name="insert" value="{#insert}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="float: right">
|
||||||
|
<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1 @@
|
|||||||
|
(function(){var a=tinymce.each;tinymce.create("tinymce.plugins.AdvListPlugin",{init:function(b,c){var d=this;d.editor=b;function e(g){var f=[];a(g.split(/,/),function(h){f.push({title:"advlist."+(h=="default"?"def":h.replace(/-/g,"_")),styles:{listStyleType:h=="default"?"":h}})});return f}d.numlist=b.getParam("advlist_number_styles")||e("default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman");d.bullist=b.getParam("advlist_bullet_styles")||e("default,circle,disc,square")},createControl:function(d,b){var f=this,e,h;if(d=="numlist"||d=="bullist"){if(f[d][0].title=="advlist.def"){h=f[d][0]}function c(i,k){var j=true;a(k.styles,function(m,l){if(f.editor.dom.getStyle(i,l)!=m){j=false;return false}});return j}function g(){var k,i=f.editor,l=i.dom,j=i.selection;k=l.getParent(j.getNode(),"ol,ul");if(!k||k.nodeName==(d=="bullist"?"OL":"UL")||c(k,h)){i.execCommand(d=="bullist"?"InsertUnorderedList":"InsertOrderedList")}if(h){k=l.getParent(j.getNode(),"ol,ul");if(k){l.setStyles(k,h.styles);k.removeAttribute("_mce_style")}}}e=b.createSplitButton(d,{title:"advanced."+d+"_desc","class":"mce_"+d,onclick:function(){g()}});e.onRenderMenu.add(function(i,j){j.onShowMenu.add(function(){var m=f.editor.dom,l=m.getParent(f.editor.selection.getNode(),"ol,ul"),k;if(l||h){k=f[d];a(j.items,function(n){var o=true;n.setSelected(0);if(l&&!n.isDisabled()){a(k,function(p){if(p.id==n.id){if(!c(l,p)){o=false;return false}}});if(o){n.setSelected(1)}}});if(!l){j.items[h.id].setSelected(1)}}});j.add({id:f.editor.dom.uniqueId(),title:"advlist.types","class":"mceMenuItemTitle"}).setDisabled(1);a(f[d],function(k){k.id=f.editor.dom.uniqueId();j.add({id:k.id,title:k.title,onclick:function(){h=k;g()}})})});return e}},getInfo:function(){return{longname:"Advanced lists",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlist",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advlist",tinymce.plugins.AdvListPlugin)})();
|
@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var each = tinymce.each;
|
||||||
|
|
||||||
|
tinymce.create('tinymce.plugins.AdvListPlugin', {
|
||||||
|
init : function(ed, url) {
|
||||||
|
var t = this;
|
||||||
|
|
||||||
|
t.editor = ed;
|
||||||
|
|
||||||
|
function buildFormats(str) {
|
||||||
|
var formats = [];
|
||||||
|
|
||||||
|
each(str.split(/,/), function(type) {
|
||||||
|
formats.push({
|
||||||
|
title : 'advlist.' + (type == 'default' ? 'def' : type.replace(/-/g, '_')),
|
||||||
|
styles : {
|
||||||
|
listStyleType : type == 'default' ? '' : type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return formats;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup number formats from config or default
|
||||||
|
t.numlist = ed.getParam("advlist_number_styles") || buildFormats("default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman");
|
||||||
|
t.bullist = ed.getParam("advlist_bullet_styles") || buildFormats("default,circle,disc,square");
|
||||||
|
},
|
||||||
|
|
||||||
|
createControl: function(name, cm) {
|
||||||
|
var t = this, btn, format;
|
||||||
|
|
||||||
|
if (name == 'numlist' || name == 'bullist') {
|
||||||
|
// Default to first item if it's a default item
|
||||||
|
if (t[name][0].title == 'advlist.def')
|
||||||
|
format = t[name][0];
|
||||||
|
|
||||||
|
function hasFormat(node, format) {
|
||||||
|
var state = true;
|
||||||
|
|
||||||
|
each(format.styles, function(value, name) {
|
||||||
|
// Format doesn't match
|
||||||
|
if (t.editor.dom.getStyle(node, name) != value) {
|
||||||
|
state = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyListFormat() {
|
||||||
|
var list, ed = t.editor, dom = ed.dom, sel = ed.selection;
|
||||||
|
|
||||||
|
// Check for existing list element
|
||||||
|
list = dom.getParent(sel.getNode(), 'ol,ul');
|
||||||
|
|
||||||
|
// Switch/add list type if needed
|
||||||
|
if (!list || list.nodeName == (name == 'bullist' ? 'OL' : 'UL') || hasFormat(list, format))
|
||||||
|
ed.execCommand(name == 'bullist' ? 'InsertUnorderedList' : 'InsertOrderedList');
|
||||||
|
|
||||||
|
// Append styles to new list element
|
||||||
|
if (format) {
|
||||||
|
list = dom.getParent(sel.getNode(), 'ol,ul');
|
||||||
|
|
||||||
|
if (list) {
|
||||||
|
dom.setStyles(list, format.styles);
|
||||||
|
list.removeAttribute('_mce_style');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
btn = cm.createSplitButton(name, {
|
||||||
|
title : 'advanced.' + name + '_desc',
|
||||||
|
'class' : 'mce_' + name,
|
||||||
|
onclick : function() {
|
||||||
|
applyListFormat();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.onRenderMenu.add(function(btn, menu) {
|
||||||
|
menu.onShowMenu.add(function() {
|
||||||
|
var dom = t.editor.dom, list = dom.getParent(t.editor.selection.getNode(), 'ol,ul'), fmtList;
|
||||||
|
|
||||||
|
if (list || format) {
|
||||||
|
fmtList = t[name];
|
||||||
|
|
||||||
|
// Unselect existing items
|
||||||
|
each(menu.items, function(item) {
|
||||||
|
var state = true;
|
||||||
|
|
||||||
|
item.setSelected(0);
|
||||||
|
|
||||||
|
if (list && !item.isDisabled()) {
|
||||||
|
each(fmtList, function(fmt) {
|
||||||
|
if (fmt.id == item.id) {
|
||||||
|
if (!hasFormat(list, fmt)) {
|
||||||
|
state = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
item.setSelected(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select the current format
|
||||||
|
if (!list)
|
||||||
|
menu.items[format.id].setSelected(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.add({id : t.editor.dom.uniqueId(), title : 'advlist.types', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
|
||||||
|
|
||||||
|
each(t[name], function(item) {
|
||||||
|
item.id = t.editor.dom.uniqueId();
|
||||||
|
|
||||||
|
menu.add({id : item.id, title : item.title, onclick : function() {
|
||||||
|
format = item;
|
||||||
|
applyListFormat();
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Advanced lists',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlist',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
tinymce.PluginManager.add('advlist', tinymce.plugins.AdvListPlugin);
|
||||||
|
})();
|
@ -0,0 +1 @@
|
|||||||
|
(function(){tinymce.create("tinymce.plugins.AutoResizePlugin",{init:function(a,c){var d=this;if(a.getParam("fullscreen_is_enabled")){return}function b(){var h=a.getDoc(),e=h.body,j=h.documentElement,g=tinymce.DOM,i=d.autoresize_min_height,f;f=tinymce.isIE?e.scrollHeight:j.offsetHeight;if(f>d.autoresize_min_height){i=f}g.setStyle(g.get(a.id+"_ifr"),"height",i+"px");if(d.throbbing){a.setProgressState(false);a.setProgressState(true)}}d.editor=a;d.autoresize_min_height=a.getElement().offsetHeight;a.onInit.add(function(f,e){f.setProgressState(true);d.throbbing=true;f.getBody().style.overflowY="hidden"});a.onChange.add(b);a.onSetContent.add(b);a.onPaste.add(b);a.onKeyUp.add(b);a.onPostRender.add(b);a.onLoadContent.add(function(f,e){b();setTimeout(function(){b();f.setProgressState(false);d.throbbing=false},1250)});a.addCommand("mceAutoResize",b)},getInfo:function(){return{longname:"Auto Resize",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autoresize",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autoresize",tinymce.plugins.AutoResizePlugin)})();
|
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
/**
|
||||||
|
* Auto Resize
|
||||||
|
*
|
||||||
|
* This plugin automatically resizes the content area to fit its content height.
|
||||||
|
* It will retain a minimum height, which is the height of the content area when
|
||||||
|
* it's initialized.
|
||||||
|
*/
|
||||||
|
tinymce.create('tinymce.plugins.AutoResizePlugin', {
|
||||||
|
/**
|
||||||
|
* Initializes the plugin, this will be executed after the plugin has been created.
|
||||||
|
* This call is done before the editor instance has finished it's initialization so use the onInit event
|
||||||
|
* of the editor instance to intercept that event.
|
||||||
|
*
|
||||||
|
* @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
|
||||||
|
* @param {string} url Absolute URL to where the plugin is located.
|
||||||
|
*/
|
||||||
|
init : function(ed, url) {
|
||||||
|
var t = this;
|
||||||
|
|
||||||
|
if (ed.getParam('fullscreen_is_enabled'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gets executed each time the editor needs to resize.
|
||||||
|
*/
|
||||||
|
function resize() {
|
||||||
|
var d = ed.getDoc(), b = d.body, de = d.documentElement, DOM = tinymce.DOM, resizeHeight = t.autoresize_min_height, myHeight;
|
||||||
|
|
||||||
|
// Get height differently depending on the browser used
|
||||||
|
myHeight = tinymce.isIE ? b.scrollHeight : de.offsetHeight;
|
||||||
|
|
||||||
|
// Don't make it smaller than the minimum height
|
||||||
|
if (myHeight > t.autoresize_min_height)
|
||||||
|
resizeHeight = myHeight;
|
||||||
|
|
||||||
|
// Resize content element
|
||||||
|
DOM.setStyle(DOM.get(ed.id + '_ifr'), 'height', resizeHeight + 'px');
|
||||||
|
|
||||||
|
// if we're throbbing, we'll re-throb to match the new size
|
||||||
|
if (t.throbbing) {
|
||||||
|
ed.setProgressState(false);
|
||||||
|
ed.setProgressState(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.editor = ed;
|
||||||
|
|
||||||
|
// Define minimum height
|
||||||
|
t.autoresize_min_height = ed.getElement().offsetHeight;
|
||||||
|
|
||||||
|
// Things to do when the editor is ready
|
||||||
|
ed.onInit.add(function(ed, l) {
|
||||||
|
// Show throbber until content area is resized properly
|
||||||
|
ed.setProgressState(true);
|
||||||
|
t.throbbing = true;
|
||||||
|
|
||||||
|
// Hide scrollbars
|
||||||
|
ed.getBody().style.overflowY = "hidden";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add appropriate listeners for resizing content area
|
||||||
|
ed.onChange.add(resize);
|
||||||
|
ed.onSetContent.add(resize);
|
||||||
|
ed.onPaste.add(resize);
|
||||||
|
ed.onKeyUp.add(resize);
|
||||||
|
ed.onPostRender.add(resize);
|
||||||
|
|
||||||
|
ed.onLoadContent.add(function(ed, l) {
|
||||||
|
resize();
|
||||||
|
|
||||||
|
// Because the content area resizes when its content CSS loads,
|
||||||
|
// and we can't easily add a listener to its onload event,
|
||||||
|
// we'll just trigger a resize after a short loading period
|
||||||
|
setTimeout(function() {
|
||||||
|
resize();
|
||||||
|
|
||||||
|
// Disable throbber
|
||||||
|
ed.setProgressState(false);
|
||||||
|
t.throbbing = false;
|
||||||
|
}, 1250);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');
|
||||||
|
ed.addCommand('mceAutoResize', resize);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about the plugin as a name/value array.
|
||||||
|
* The current keys are longname, author, authorurl, infourl and version.
|
||||||
|
*
|
||||||
|
* @return {Object} Name/value array containing information about the plugin.
|
||||||
|
*/
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Auto Resize',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autoresize',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
tinymce.PluginManager.add('autoresize', tinymce.plugins.AutoResizePlugin);
|
||||||
|
})();
|
@ -0,0 +1 @@
|
|||||||
|
(function(e){var c="autosave",g="restoredraft",b=true,f,d,a=e.util.Dispatcher;e.PluginManager.requireLangPack(c);e.create("tinymce.plugins.AutoSave",{init:function(i,j){var h=this,l=i.settings;h.editor=i;function k(n){var m={s:1000,m:60000};n=/^(\d+)([ms]?)$/.exec(""+n);return(n[2]?m[n[2]]:1)*parseInt(n)}e.each({ask_before_unload:b,interval:"30s",retention:"20m",minlength:50},function(n,m){m=c+"_"+m;if(l[m]===f){l[m]=n}});l.autosave_interval=k(l.autosave_interval);l.autosave_retention=k(l.autosave_retention);i.addButton(g,{title:c+".restore_content",onclick:function(){if(i.getContent().replace(/\s| |<\/?p[^>]*>|<br[^>]*>/gi,"").length>0){i.windowManager.confirm(c+".warning_message",function(m){if(m){h.restoreDraft()}})}else{h.restoreDraft()}}});i.onNodeChange.add(function(){var m=i.controlManager;if(m.get(g)){m.setDisabled(g,!h.hasDraft())}});i.onInit.add(function(){if(i.controlManager.get(g)){h.setupStorage(i);setInterval(function(){h.storeDraft();i.nodeChanged()},l.autosave_interval)}});h.onStoreDraft=new a(h);h.onRestoreDraft=new a(h);h.onRemoveDraft=new a(h);if(!d){window.onbeforeunload=e.plugins.AutoSave._beforeUnloadHandler;d=b}},getInfo:function(){return{longname:"Auto save",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave",version:e.majorVersion+"."+e.minorVersion}},getExpDate:function(){return new Date(new Date().getTime()+this.editor.settings.autosave_retention).toUTCString()},setupStorage:function(i){var h=this,k=c+"_test",j="OK";h.key=c+i.id;e.each([function(){if(localStorage){localStorage.setItem(k,j);if(localStorage.getItem(k)===j){localStorage.removeItem(k);return localStorage}}},function(){if(sessionStorage){sessionStorage.setItem(k,j);if(sessionStorage.getItem(k)===j){sessionStorage.removeItem(k);return sessionStorage}}},function(){if(e.isIE){i.getElement().style.behavior="url('#default#userData')";return{autoExpires:b,setItem:function(l,n){var m=i.getElement();m.setAttribute(l,n);m.expires=h.getExpDate();m.save("TinyMCE")},getItem:function(l){var m=i.getElement();m.load("TinyMCE");return m.getAttribute(l)},removeItem:function(l){i.getElement().removeAttribute(l)}}}},],function(l){try{h.storage=l();if(h.storage){return false}}catch(m){}})},storeDraft:function(){var i=this,l=i.storage,j=i.editor,h,k;if(l){if(!l.getItem(i.key)&&!j.isDirty()){return}k=j.getContent();if(k.length>j.settings.autosave_minlength){h=i.getExpDate();if(!i.storage.autoExpires){i.storage.setItem(i.key+"_expires",h)}i.storage.setItem(i.key,k);i.onStoreDraft.dispatch(i,{expires:h,content:k})}}},restoreDraft:function(){var h=this,i=h.storage;if(i){content=i.getItem(h.key);if(content){h.editor.setContent(content);h.onRestoreDraft.dispatch(h,{content:content})}}},hasDraft:function(){var h=this,k=h.storage,i,j;if(k){j=!!k.getItem(h.key);if(j){if(!h.storage.autoExpires){i=new Date(k.getItem(h.key+"_expires"));if(new Date().getTime()<i.getTime()){return b}h.removeDraft()}else{return b}}}return false},removeDraft:function(){var h=this,k=h.storage,i=h.key,j;if(k){j=k.getItem(i);k.removeItem(i);k.removeItem(i+"_expires");if(j){h.onRemoveDraft.dispatch(h,{content:j})}}},"static":{_beforeUnloadHandler:function(h){var i;e.each(tinyMCE.editors,function(j){if(j.plugins.autosave){j.plugins.autosave.storeDraft()}if(j.getParam("fullscreen_is_enabled")){return}if(!i&&j.isDirty()&&j.getParam("autosave_ask_before_unload")){i=j.getLang("autosave.unload_msg")}});return i}}});e.PluginManager.add("autosave",e.plugins.AutoSave)})(tinymce);
|
@ -0,0 +1,425 @@
|
|||||||
|
/**
|
||||||
|
* editor_plugin_src.js
|
||||||
|
*
|
||||||
|
* Copyright 2009, Moxiecode Systems AB
|
||||||
|
* Released under LGPL License.
|
||||||
|
*
|
||||||
|
* License: http://tinymce.moxiecode.com/license
|
||||||
|
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||||
|
*
|
||||||
|
* Adds auto-save capability to the TinyMCE text editor to rescue content
|
||||||
|
* inadvertently lost. This plugin was originally developed by Speednet
|
||||||
|
* and that project can be found here: http://code.google.com/p/tinyautosave/
|
||||||
|
*
|
||||||
|
* TECHNOLOGY DISCUSSION:
|
||||||
|
*
|
||||||
|
* The plugin attempts to use the most advanced features available in the current browser to save
|
||||||
|
* as much content as possible. There are a total of four different methods used to autosave the
|
||||||
|
* content. In order of preference, they are:
|
||||||
|
*
|
||||||
|
* 1. localStorage - A new feature of HTML 5, localStorage can store megabytes of data per domain
|
||||||
|
* on the client computer. Data stored in the localStorage area has no expiration date, so we must
|
||||||
|
* manage expiring the data ourselves. localStorage is fully supported by IE8, and it is supposed
|
||||||
|
* to be working in Firefox 3 and Safari 3.2, but in reality is is flaky in those browsers. As
|
||||||
|
* HTML 5 gets wider support, the AutoSave plugin will use it automatically. In Windows Vista/7,
|
||||||
|
* localStorage is stored in the following folder:
|
||||||
|
* C:\Users\[username]\AppData\Local\Microsoft\Internet Explorer\DOMStore\[tempFolder]
|
||||||
|
*
|
||||||
|
* 2. sessionStorage - A new feature of HTML 5, sessionStorage works similarly to localStorage,
|
||||||
|
* except it is designed to expire after a certain amount of time. Because the specification
|
||||||
|
* around expiration date/time is very loosely-described, it is preferrable to use locaStorage and
|
||||||
|
* manage the expiration ourselves. sessionStorage has similar storage characteristics to
|
||||||
|
* localStorage, although it seems to have better support by Firefox 3 at the moment. (That will
|
||||||
|
* certainly change as Firefox continues getting better at HTML 5 adoption.)
|
||||||
|
*
|
||||||
|
* 3. UserData - A very under-exploited feature of Microsoft Internet Explorer, UserData is a
|
||||||
|
* way to store up to 128K of data per "document", or up to 1MB of data per domain, on the client
|
||||||
|
* computer. The feature is available for IE 5+, which makes it available for every version of IE
|
||||||
|
* supported by TinyMCE. The content is persistent across browser restarts and expires on the
|
||||||
|
* date/time specified, just like a cookie. However, the data is not cleared when the user clears
|
||||||
|
* cookies on the browser, which makes it well-suited for rescuing autosaved content. UserData,
|
||||||
|
* like other Microsoft IE browser technologies, is implemented as a behavior attached to a
|
||||||
|
* specific DOM object, so in this case we attach the behavior to the same DOM element that the
|
||||||
|
* TinyMCE editor instance is attached to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(tinymce) {
|
||||||
|
// Setup constants to help the compressor to reduce script size
|
||||||
|
var PLUGIN_NAME = 'autosave',
|
||||||
|
RESTORE_DRAFT = 'restoredraft',
|
||||||
|
TRUE = true,
|
||||||
|
undefined,
|
||||||
|
unloadHandlerAdded,
|
||||||
|
Dispatcher = tinymce.util.Dispatcher;
|
||||||
|
|
||||||
|
// Load language pack for the autosave plugin
|
||||||
|
tinymce.PluginManager.requireLangPack(PLUGIN_NAME);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This plugin adds auto-save capability to the TinyMCE text editor to rescue content
|
||||||
|
* inadvertently lost. By using localStorage.
|
||||||
|
*
|
||||||
|
* @class tinymce.plugins.AutoSave
|
||||||
|
*/
|
||||||
|
tinymce.create('tinymce.plugins.AutoSave', {
|
||||||
|
/**
|
||||||
|
* Initializes the plugin, this will be executed after the plugin has been created.
|
||||||
|
* This call is done before the editor instance has finished it's initialization so use the onInit event
|
||||||
|
* of the editor instance to intercept that event.
|
||||||
|
*
|
||||||
|
* @method init
|
||||||
|
* @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
|
||||||
|
* @param {string} url Absolute URL to where the plugin is located.
|
||||||
|
*/
|
||||||
|
init : function(ed, url) {
|
||||||
|
var self = this, settings = ed.settings;
|
||||||
|
|
||||||
|
self.editor = ed;
|
||||||
|
|
||||||
|
// Parses the specified time string into a milisecond number 10m, 10s etc.
|
||||||
|
function parseTime(time) {
|
||||||
|
var multipels = {
|
||||||
|
s : 1000,
|
||||||
|
m : 60000
|
||||||
|
};
|
||||||
|
|
||||||
|
time = /^(\d+)([ms]?)$/.exec('' + time);
|
||||||
|
|
||||||
|
return (time[2] ? multipels[time[2]] : 1) * parseInt(time);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default config
|
||||||
|
tinymce.each({
|
||||||
|
ask_before_unload : TRUE,
|
||||||
|
interval : '30s',
|
||||||
|
retention : '20m',
|
||||||
|
minlength : 50
|
||||||
|
}, function(value, key) {
|
||||||
|
key = PLUGIN_NAME + '_' + key;
|
||||||
|
|
||||||
|
if (settings[key] === undefined)
|
||||||
|
settings[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse times
|
||||||
|
settings.autosave_interval = parseTime(settings.autosave_interval);
|
||||||
|
settings.autosave_retention = parseTime(settings.autosave_retention);
|
||||||
|
|
||||||
|
// Register restore button
|
||||||
|
ed.addButton(RESTORE_DRAFT, {
|
||||||
|
title : PLUGIN_NAME + ".restore_content",
|
||||||
|
onclick : function() {
|
||||||
|
if (ed.getContent().replace(/\s| |<\/?p[^>]*>|<br[^>]*>/gi, "").length > 0) {
|
||||||
|
// Show confirm dialog if the editor isn't empty
|
||||||
|
ed.windowManager.confirm(
|
||||||
|
PLUGIN_NAME + ".warning_message",
|
||||||
|
function(ok) {
|
||||||
|
if (ok)
|
||||||
|
self.restoreDraft();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else
|
||||||
|
self.restoreDraft();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable/disable restoredraft button depending on if there is a draft stored or not
|
||||||
|
ed.onNodeChange.add(function() {
|
||||||
|
var controlManager = ed.controlManager;
|
||||||
|
|
||||||
|
if (controlManager.get(RESTORE_DRAFT))
|
||||||
|
controlManager.setDisabled(RESTORE_DRAFT, !self.hasDraft());
|
||||||
|
});
|
||||||
|
|
||||||
|
ed.onInit.add(function() {
|
||||||
|
// Check if the user added the restore button, then setup auto storage logic
|
||||||
|
if (ed.controlManager.get(RESTORE_DRAFT)) {
|
||||||
|
// Setup storage engine
|
||||||
|
self.setupStorage(ed);
|
||||||
|
|
||||||
|
// Auto save contents each interval time
|
||||||
|
setInterval(function() {
|
||||||
|
self.storeDraft();
|
||||||
|
ed.nodeChanged();
|
||||||
|
}, settings.autosave_interval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event gets fired when a draft is stored to local storage.
|
||||||
|
*
|
||||||
|
* @event onStoreDraft
|
||||||
|
* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
|
||||||
|
* @param {Object} draft Draft object containing the HTML contents of the editor.
|
||||||
|
*/
|
||||||
|
self.onStoreDraft = new Dispatcher(self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event gets fired when a draft is restored from local storage.
|
||||||
|
*
|
||||||
|
* @event onStoreDraft
|
||||||
|
* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
|
||||||
|
* @param {Object} draft Draft object containing the HTML contents of the editor.
|
||||||
|
*/
|
||||||
|
self.onRestoreDraft = new Dispatcher(self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event gets fired when a draft removed/expired.
|
||||||
|
*
|
||||||
|
* @event onRemoveDraft
|
||||||
|
* @param {tinymce.plugins.AutoSave} sender Plugin instance sending the event.
|
||||||
|
* @param {Object} draft Draft object containing the HTML contents of the editor.
|
||||||
|
*/
|
||||||
|
self.onRemoveDraft = new Dispatcher(self);
|
||||||
|
|
||||||
|
// Add ask before unload dialog only add one unload handler
|
||||||
|
if (!unloadHandlerAdded) {
|
||||||
|
window.onbeforeunload = tinymce.plugins.AutoSave._beforeUnloadHandler;
|
||||||
|
unloadHandlerAdded = TRUE;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about the plugin as a name/value array.
|
||||||
|
* The current keys are longname, author, authorurl, infourl and version.
|
||||||
|
*
|
||||||
|
* @method getInfo
|
||||||
|
* @return {Object} Name/value array containing information about the plugin.
|
||||||
|
*/
|
||||||
|
getInfo : function() {
|
||||||
|
return {
|
||||||
|
longname : 'Auto save',
|
||||||
|
author : 'Moxiecode Systems AB',
|
||||||
|
authorurl : 'http://tinymce.moxiecode.com',
|
||||||
|
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave',
|
||||||
|
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an expiration date UTC string.
|
||||||
|
*
|
||||||
|
* @method getExpDate
|
||||||
|
* @return {String} Expiration date UTC string.
|
||||||
|
*/
|
||||||
|
getExpDate : function() {
|
||||||
|
return new Date(
|
||||||
|
new Date().getTime() + this.editor.settings.autosave_retention
|
||||||
|
).toUTCString();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will setup the storage engine. If the browser has support for it.
|
||||||
|
*
|
||||||
|
* @method setupStorage
|
||||||
|
*/
|
||||||
|
setupStorage : function(ed) {
|
||||||
|
var self = this, testKey = PLUGIN_NAME + '_test', testVal = "OK";
|
||||||
|
|
||||||
|
self.key = PLUGIN_NAME + ed.id;
|
||||||
|
|
||||||
|
// Loop though each storage engine type until we find one that works
|
||||||
|
tinymce.each([
|
||||||
|
function() {
|
||||||
|
// Try HTML5 Local Storage
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem(testKey, testVal);
|
||||||
|
|
||||||
|
if (localStorage.getItem(testKey) === testVal) {
|
||||||
|
localStorage.removeItem(testKey);
|
||||||
|
|
||||||
|
return localStorage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
function() {
|
||||||
|
// Try HTML5 Session Storage
|
||||||
|
if (sessionStorage) {
|
||||||
|
sessionStorage.setItem(testKey, testVal);
|
||||||
|
|
||||||
|
if (sessionStorage.getItem(testKey) === testVal) {
|
||||||
|
sessionStorage.removeItem(testKey);
|
||||||
|
|
||||||
|
return sessionStorage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
function() {
|
||||||
|
// Try IE userData
|
||||||
|
if (tinymce.isIE) {
|
||||||
|
ed.getElement().style.behavior = "url('#default#userData')";
|
||||||
|
|
||||||
|
// Fake localStorage on old IE
|
||||||
|
return {
|
||||||
|
autoExpires : TRUE,
|
||||||
|
|
||||||
|
setItem : function(key, value) {
|
||||||
|
var userDataElement = ed.getElement();
|
||||||
|
|
||||||
|
userDataElement.setAttribute(key, value);
|
||||||
|
userDataElement.expires = self.getExpDate();
|
||||||
|
userDataElement.save("TinyMCE");
|
||||||
|
},
|
||||||
|
|
||||||
|
getItem : function(key) {
|
||||||
|
var userDataElement = ed.getElement();
|
||||||
|
|
||||||
|
userDataElement.load("TinyMCE");
|
||||||
|
|
||||||
|
return userDataElement.getAttribute(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeItem : function(key) {
|
||||||
|
ed.getElement().removeAttribute(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], function(setup) {
|
||||||
|
// Try executing each function to find a suitable storage engine
|
||||||
|
try {
|
||||||
|
self.storage = setup();
|
||||||
|
|
||||||
|
if (self.storage)
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will store the current contents in the the storage engine.
|
||||||
|
*
|
||||||
|
* @method storeDraft
|
||||||
|
*/
|
||||||
|
storeDraft : function() {
|
||||||
|
var self = this, storage = self.storage, editor = self.editor, expires, content;
|
||||||
|
|
||||||
|
// Is the contents dirty
|
||||||
|
if (storage) {
|
||||||
|
// If there is no existing key and the contents hasn't been changed since
|
||||||
|
// it's original value then there is no point in saving a draft
|
||||||
|
if (!storage.getItem(self.key) && !editor.isDirty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Store contents if the contents if longer than the minlength of characters
|
||||||
|
content = editor.getContent();
|
||||||
|
if (content.length > editor.settings.autosave_minlength) {
|
||||||
|
expires = self.getExpDate();
|
||||||
|
|
||||||
|
// Store expiration date if needed IE userData has auto expire built in
|
||||||
|
if (!self.storage.autoExpires)
|
||||||
|
self.storage.setItem(self.key + "_expires", expires);
|
||||||
|
|
||||||
|
self.storage.setItem(self.key, content);
|
||||||
|
self.onStoreDraft.dispatch(self, {
|
||||||
|
expires : expires,
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will restore the contents from the storage engine back to the editor.
|
||||||
|
*
|
||||||
|
* @method restoreDraft
|
||||||
|
*/
|
||||||
|
restoreDraft : function() {
|
||||||
|
var self = this, storage = self.storage;
|
||||||
|
|
||||||
|
if (storage) {
|
||||||
|
content = storage.getItem(self.key);
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
self.editor.setContent(content);
|
||||||
|
self.onRestoreDraft.dispatch(self, {
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return true/false if there is a local storage draft available.
|
||||||
|
*
|
||||||
|
* @method hasDraft
|
||||||
|
* @return {boolean} true/false state if there is a local draft.
|
||||||
|
*/
|
||||||
|
hasDraft : function() {
|
||||||
|
var self = this, storage = self.storage, expDate, exists;
|
||||||
|
|
||||||
|
if (storage) {
|
||||||
|
// Does the item exist at all
|
||||||
|
exists = !!storage.getItem(self.key);
|
||||||
|
if (exists) {
|
||||||
|
// Storage needs autoexpire
|
||||||
|
if (!self.storage.autoExpires) {
|
||||||
|
expDate = new Date(storage.getItem(self.key + "_expires"));
|
||||||
|
|
||||||
|
// Contents hasn't expired
|
||||||
|
if (new Date().getTime() < expDate.getTime())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
// Remove it if it has
|
||||||
|
self.removeDraft();
|
||||||
|
} else
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the currently stored draft.
|
||||||
|
*
|
||||||
|
* @method removeDraft
|
||||||
|
*/
|
||||||
|
removeDraft : function() {
|
||||||
|
var self = this, storage = self.storage, key = self.key, content;
|
||||||
|
|
||||||
|
if (storage) {
|
||||||
|
// Get current contents and remove the existing draft
|
||||||
|
content = storage.getItem(key);
|
||||||
|
storage.removeItem(key);
|
||||||
|
storage.removeItem(key + "_expires");
|
||||||
|
|
||||||
|
// Dispatch remove event if we had any contents
|
||||||
|
if (content) {
|
||||||
|
self.onRemoveDraft.dispatch(self, {
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"static" : {
|
||||||
|
// Internal unload handler will be called before the page is unloaded
|
||||||
|
_beforeUnloadHandler : function(e) {
|
||||||
|
var msg;
|
||||||
|
|
||||||
|
tinymce.each(tinyMCE.editors, function(ed) {
|
||||||
|
// Store a draft for each editor instance
|
||||||
|
if (ed.plugins.autosave)
|
||||||
|
ed.plugins.autosave.storeDraft();
|
||||||
|
|
||||||
|
// Never ask in fullscreen mode
|
||||||
|
if (ed.getParam("fullscreen_is_enabled"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup a return message if the editor is dirty
|
||||||
|
if (!msg && ed.isDirty() && ed.getParam("autosave_ask_before_unload"))
|
||||||
|
msg = ed.getLang("autosave.unload_msg");
|
||||||
|
});
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tinymce.PluginManager.add('autosave', tinymce.plugins.AutoSave);
|
||||||
|
})(tinymce);
|