WebFlash-0.1a9/0000755000076500000240000000000011157175137012622 5ustar albertostaffWebFlash-0.1a9/MANIFEST.in0000644000076500000240000000004011134224300014330 0ustar albertostaffrecursive-include webflash *.js WebFlash-0.1a9/PKG-INFO0000644000076500000240000000111511157175137013715 0ustar albertostaffMetadata-Version: 1.0 Name: WebFlash Version: 0.1a9 Summary: Portable flash messages for WSGI apps Home-page: http://python-rum.org/wiki/WebFlash Author: Alberto Valverde Gonzalez Author-email: alberto@python-rum.org License: MIT Description: UNKNOWN Keywords: wsgi flash webob Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python WebFlash-0.1a9/setup.cfg0000644000076500000240000000026111157175137014442 0ustar albertostaff[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [nosetests] cover-package = webflash with-coverage = false with-doctest = true [aliases] release = egg_info -RDb "" WebFlash-0.1a9/setup.py0000644000076500000240000000201711157175012014324 0ustar albertostafffrom setuptools import setup, find_packages import sys, os version = '0.1a9' install_requires=[] if sys.version_info[:2] < (2,6): install_requires.append("simplejson") setup( name='WebFlash', version=version, description="Portable flash messages for WSGI apps", long_description="", keywords='wsgi flash webob', author='Alberto Valverde Gonzalez', author_email='alberto@python-rum.org', url='http://python-rum.org/wiki/WebFlash', license='MIT', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, install_requires=install_requires, tests_require=["WebTest", "nose"], test_suite="nose.collector", zip_safe=False, classifiers = [ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Topic :: Software Development :: Libraries :: Python Modules', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Programming Language :: Python', ], ) WebFlash-0.1a9/webflash/0000755000076500000240000000000011157175137014415 5ustar albertostaffWebFlash-0.1a9/webflash/__init__.py0000644000076500000240000001736011157174774016543 0ustar albertostaffimport os from urllib import quote, unquote from logging import getLogger try: import json except ImportError: # Python < 2.6 import simplejson as json __all__ = ["Flash"] IS_DEBUG = 'WEBFLASH_DEBUG' in os.environ log = getLogger(__name__) class Flash(object): """ A flash message creator. *Parameters (all optional):* `cookie_name` The name of the cookie that will be used as transport `default_status` The CSS class that will be added to the flash container when it is not passed explicitly when calling the Flash instance. `get_response` A callable that will provide the response object of the framework if no explicit response is passed when calling the Flash instance. `js_path` Shall you want to override the JS code that will display the flash message then pass the path to where the file lives. Note that this is a path within the file-system, not a URL `debug` Should the JS code be provided in an uncompressed form (provided that js_path has not been overriden) and reloaded every time a page is rendered? Flash needs a Reponse object with a `set_cookie` method, like WebOb's :class:`webob.Response`. We'll mock one for demonstration purposes:: >>> class MyResponse(object): ... def set_cookie(self, name, value): ... pass The :class:`Flash` object could be instantiated once for the lifetime of an application and be kept at module scope (or a request-local object if the framework provides one). A :meth:`get_response` callable is useful in this case if the framework provides a global response to avoid passing the response object around on every call to the :class:`Flash` instance:: >>> flash = Flash(get_response=lambda: MyResponse()) Displaying the flash message is done by calling the Flash instance from your controller/view code:: >>> flash("All your data has been erased and your identity destroyed") >>> flash("Stuff broke", status="error") To insert the JavaScript code needed for Flash to work you need to insert the result of the :meth:`render` method on every page you might want to display flash messages (normally all of them, so you might want to include the call in a base template):: >>> _ = flash.render("flash-container") """ template = '
'\ ''\ '
' static_template = '
'\ '
%(message)s
'\ '
' _js_code = None def __init__(self, cookie_name="webflash", default_status="ok", get_response=None, get_request=None, js_path=None, debug=IS_DEBUG): self.default_status = default_status self.get_response = get_response or (lambda: None) self.get_request = get_request or (lambda: None) self.cookie_name = cookie_name self.js_path = js_path or _get_js_path(debug) if not debug: # Preload js_code so we don't need to read the file on every request self.js_code = open(self.js_path).read() def _get_js_code(self): return self._js_code or open(self.js_path).read() def _set_js_code(self, value): self._js_code = value js_code = property(_get_js_code, _set_js_code) def __call__(self, message, status=None, response=None, request=None, **extra_payload): response = response or self.get_response() if response is None: raise ValueError( "Must provide a response object or configure a callable that " "provides one" ) payload = self.prepare_payload( message = message, status = status or self.default_status, **extra_payload ) request = request or self.get_request() if request is not None: # Save the payload in environ too in case JavaScript is not being # used and the message is being displayed in the same request. request.environ['webflash.payload'] = payload log.debug("Setting payload in environ %d", id(request.environ)) log.debug("Setting payload in cookie") response.set_cookie(self.cookie_name, payload) def prepare_payload(self, **data): return quote(json.dumps(data)) def js_call(self, container_id): return 'webflash(%(options)s).render();' % { 'options': json.dumps({ 'id': container_id, 'name': self.cookie_name }) } def render(self, container_id, use_js=True, request=None, response=None): if use_js: return self._render_js_version(container_id) else: return self._render_static_version(container_id, request, response) def _render_static_version(self, container_id, request, response): payload = self.pop_payload(request, response) if not payload: return '' payload['message'] = html_escape(payload.get('message','')) payload['container_id'] = container_id return self.static_template % payload def _render_js_version(self, container_id): return self.template % { 'container_id': container_id, 'js_code': self.js_code, 'js_call': self.js_call(container_id) } def pop_payload(self, request=None, response=None): """ Fetches and decodes the flash payload from the request and sets the required Set-Cookie header in the response so the browser deletes the flash cookie. This method is intended to manage the flash payload without using JavaScript and requires webob compatible request/response objects. """ # First try fetching it from the request request = request or self.get_request() response = response or self.get_response() if request is None or response is None: raise ValueError("Need to provide a request and reponse objects") payload = request.environ.get('webflash.payload', {}) if not payload: payload = request.str_cookies.get(self.cookie_name, {}) if payload: log.debug("Got payload from cookie") else: log.debug("Got payload for environ %d, %r", id(request.environ), payload) if payload: payload = json.loads(unquote(payload)) if 'webflash.deleted_cookie' not in request.environ: log.debug("Deleting flash payload") response.delete_cookie(self.cookie_name) request.environ['webflash.delete_cookie'] = True return payload or {} html_escape_table = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", } # From http://wiki.python.org/moin/EscapingHtml def html_escape(text): """Produce entities within text.""" L=[] for c in text: L.append(html_escape_table.get(c,c)) return "".join(L) def _get_js_path(debug): filename = debug and 'webflash.js' or 'webflash.min.js' try: import pkg_resources return pkg_resources.resource_filename('webflash', filename) except ImportError: # Be compatible with pkg_resources haters return os.path.abspath( os.path.join(os.path.dirname(__file__), filename) ) WebFlash-0.1a9/webflash/webflash.js0000644000076500000240000002130011134621326016531 0ustar albertostaff/* webflash.js * * Copyright (c) 2009 Alberto Valverde González * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * USAGE: * var wf = webflash(options); * * Available options: * id -> The id of the DOM node that acts as a container of * the flash message. This element must exist in the DOM. * Will default to "webflash" if missing from hash. * * name -> The name of the cookie that carries the payload. * Will default to "webflash" if missing from hash. * * on_display -> (optional) Callback to execute after the flash message * has been displayed. It is called with the payload * and the container DOM node as first and second * arguments. * If this option is a string it is expected to be the * name of a function attached to the window object * create_node -> (optional) A function that should return the DOM node * that will be appended to the container. * If this option is a string it is expected to be the * name of a function attached to the window object * * Available methods: * * wf.render() -> Renders the flash message stored in the flash cookie. * This function must be called before the window has * finisihed loading. * If the cookie was not set by the server this is a noop. * * wf.payload() -> Returns an object with the payload stored in the cookie * If no cookie was set returns null. * * Payload structure: * * The payload is expected to be an escaped JSON string which deserializes * into an object with the following keys: * * message (string) -> The text to insert into the container. By default this * text will be apended as a text node so HTML will be * escaped. Initialize webflash with a custom * 'create_node' function to override this. * status (string) -> If present this will be the class of the div that * holds the flash message inside the container. * delay (int) -> If present, the flash container will be hidden after * this amount of milliseconds. * * Extra arguments may be passed in th payload and interpreted by a custom * create_node or on_display function. * */ if (!window.webflash) { webflash = (function() { var doc = document; // Alias to aid name-mangling var allCookies = doc.cookie; var cookie_name = null; var flash_shown = false; var container_id = null; var isIE = /msie|MSIE/.test(navigator.userAgent); // This function creates the node that will hold the flash message inside // the flash container. It can be overrided with the options.create_node // option passed to webflash() var create_node = function (payload) { return doc.createTextNode(payload.message); } var on_display = function(payload, node) {}; var lookup_method = function(name_or_func, _default) { var ret = _default; if (typeof(name_or_func) == "string") { ret = window[name_or_func]; } else if (name_or_func) { ret = name_or_func; } return ret } var get_payload = function() { var pos = allCookies.indexOf(cookie_name + '='); if (pos<0) { return null; } var start = pos + cookie_name.length + 1; var end = allCookies.indexOf(';', start); if (end == -1) { end = allCookies.length; } var cookie = allCookies.substring(start, end); // remove cookie doc.cookie = cookie_name + '=; expires=Fri, 02-Jan-1970 00:00:00 GMT; path=/'; return webflash.lj(unescape(cookie)); } var display_flash = function() { if (flash_shown) return; flash_shown = true; var payload = get_payload(); if (payload !== null) { var container = doc.getElementById(container_id); var flash_div = doc.createElement('div'); if (payload.status) { flash_div.setAttribute(isIE?'className':'class', payload.status); } var messageNode = create_node(payload); flash_div.appendChild(messageNode); container.style.display = 'block'; if (payload.delay) { setTimeout(function() { container.style.display = 'none'; }, payload.delay); } container.appendChild(flash_div); on_display(payload, container); } } // Adds a display_flash for when the DOM is ready. It also adds a // callback for the window "onload" event since the domready event does // not always work. // This code is heavily inspired by jquery's ready() function. var attachLoadEvent = function() { if (!isIE) { // DOM 2 Event model var domloaded = "DOMContentLoaded"; doc.addEventListener(domloaded, function() { doc.removeEventListener(domloaded, arguments.callee, false); display_flash(); }, false); // A fallback to window.onload that will always work window.addEventListener("load", display_flash, false); } else if (isIE) { // IE event model var domloaded = "onreadystatechange"; // ensure firing before onload, // maybe late but safe also for iframes doc.attachEvent(domloaded, function() { doc.detachEvent(domloaded, arguments.callee); display_flash(); }); // If IE and not an iframe // continually check to see if the document is ready if (doc.documentElement.doScroll && !frameElement ) (function(){ if (flash_shown) return; try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ doc.documentElement.doScroll("left"); } catch( error ) { setTimeout( arguments.callee, 0 ); return; } display_flash() })(); // A fallback to window.onload that will always work window.attachEvent("load", display_flash); } } return function(opts) { cookie_name = opts.name || "webflash"; container_id = opts.id || "webflash"; on_display = lookup_method(opts.on_display, on_display); create_node = lookup_method(opts.create_node, create_node); return { payload: get_payload, render: attachLoadEvent } } })(); // This function needs to live outside the anonymous function's closure for // YUICompressor to be able to mangle the private symbols because it uses // eval webflash.lj = function(s) { // Loads a JSON string var r; eval("r="+s); return r; }; }; WebFlash-0.1a9/webflash/webflash.min.js0000644000076500000240000000301211134621536017316 0ustar albertostaffif(!window.webflash){webflash=(function(){var j=document;var k=j.cookie;var f=null;var e=false;var g=null;var c=/msie|MSIE/.test(navigator.userAgent);var a=function(m){return j.createTextNode(m.message)};var l=function(n,m){};var b=function(o,m){var n=m;if(typeof(o)=="string"){n=window[o]}else{if(o){n=o}}return n};var h=function(){var p=k.indexOf(f+"=");if(p<0){return null}var o=p+f.length+1;var m=k.indexOf(";",o);if(m==-1){m=k.length}var n=k.substring(o,m);j.cookie=f+"=; expires=Fri, 02-Jan-1970 00:00:00 GMT; path=/";return webflash.lj(unescape(n))};var i=function(){if(e){return}e=true;var p=h();if(p!==null){var m=j.getElementById(g);var n=j.createElement("div");if(p.status){n.setAttribute(c?"className":"class",p.status)}var o=a(p);n.appendChild(o);m.style.display="block";if(p.delay){setTimeout(function(){m.style.display="none"},p.delay)}m.appendChild(n);l(p,m)}};var d=function(){if(!c){var m="DOMContentLoaded";j.addEventListener(m,function(){j.removeEventListener(m,arguments.callee,false);i()},false);window.addEventListener("load",i,false)}else{if(c){var m="onreadystatechange";j.attachEvent(m,function(){j.detachEvent(m,arguments.callee);i()});if(j.documentElement.doScroll&&!frameElement){(function(){if(e){return}try{j.documentElement.doScroll("left")}catch(n){setTimeout(arguments.callee,0);return}i()})()}window.attachEvent("load",i)}}};return function(m){f=m.name||"webflash";g=m.id||"webflash";l=b(m.on_display,l);a=b(m.create_node,a);return{payload:h,render:d}}})();webflash.lj=function(s){var r;eval("r="+s);return r}};WebFlash-0.1a9/WebFlash.egg-info/0000755000076500000240000000000011157175137016007 5ustar albertostaffWebFlash-0.1a9/WebFlash.egg-info/dependency_links.txt0000644000076500000240000000000111157175137022055 0ustar albertostaff WebFlash-0.1a9/WebFlash.egg-info/not-zip-safe0000644000076500000240000000000111134156407020227 0ustar albertostaff WebFlash-0.1a9/WebFlash.egg-info/PKG-INFO0000644000076500000240000000111511157175137017102 0ustar albertostaffMetadata-Version: 1.0 Name: WebFlash Version: 0.1a9 Summary: Portable flash messages for WSGI apps Home-page: http://python-rum.org/wiki/WebFlash Author: Alberto Valverde Gonzalez Author-email: alberto@python-rum.org License: MIT Description: UNKNOWN Keywords: wsgi flash webob Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python WebFlash-0.1a9/WebFlash.egg-info/requires.txt0000644000076500000240000000001211157175137020400 0ustar albertostaffsimplejsonWebFlash-0.1a9/WebFlash.egg-info/SOURCES.txt0000644000076500000240000000043711157175137017677 0ustar albertostaffMANIFEST.in setup.cfg setup.py WebFlash.egg-info/PKG-INFO WebFlash.egg-info/SOURCES.txt WebFlash.egg-info/dependency_links.txt WebFlash.egg-info/not-zip-safe WebFlash.egg-info/requires.txt WebFlash.egg-info/top_level.txt webflash/__init__.py webflash/webflash.js webflash/webflash.min.jsWebFlash-0.1a9/WebFlash.egg-info/top_level.txt0000644000076500000240000000001111157175137020531 0ustar albertostaffwebflash