diff --git a/web/.project b/web/.project new file mode 100644 index 0000000..880d08e --- /dev/null +++ b/web/.project @@ -0,0 +1,22 @@ + + + web + + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.dltk.core.scriptbuilder + + + + + + org.eclipse.php.core.PHPNature + + diff --git a/web/blocks.sh b/web/blocks.sh new file mode 100755 index 0000000..e6e9b7f --- /dev/null +++ b/web/blocks.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cd /var/web +while true; do + php5 run.php cronjob/runblocks + sleep 20 +done +exec bash + + diff --git a/web/extensions/jqplot/MIT-LICENSE.txt b/web/extensions/jqplot/MIT-LICENSE.txt new file mode 100644 index 0000000..f8111b9 --- /dev/null +++ b/web/extensions/jqplot/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +Title: MIT License + +Copyright (c) 2009-2013 Chris Leonello + +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. \ No newline at end of file diff --git a/web/extensions/jqplot/README.txt b/web/extensions/jqplot/README.txt new file mode 100644 index 0000000..8777a20 --- /dev/null +++ b/web/extensions/jqplot/README.txt @@ -0,0 +1,77 @@ +Title: jqPlot Readme + +Pure JavaScript plotting plugin for jQuery. + +To learn how to use jqPlot, start with the Basic Usage Instructions below. Then read the +usage.txt and jqPlotOptions.txt files included with the distribution. + +The jqPlot home page is at . + +Downloads can be found at . + +The mailing list is at . + +Examples and unit tests are at . + +Documentation is at . + +The project page and source code are at . + +Bugs, issues, feature requests: . + +Basic Usage Instructions: + +jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.9.1 is included in +the distribution. To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and +optionally the excanvas script to support IE version prior to IE 9 in your web page: + +> +> +> +> + +For usage instructions, see in usage.txt. For available options, see + in jqPlotOptions.txt. + +Building from source: + +If you've cloned the repository, you can build a distribution from source. +You need to have ant installed. You can simply +type "ant" from the jqplot directory to build the default "all" target. +There are 6 pertinent targets: clean, dist, min, docs, compress and all. Use: + +> ant -p + +to get a description of the various build targets. + +Legal Notices: + +Copyright (c) 2009-2013 Chris Leonello +jqPlot is currently available for use in all personal or commercial projects +under both the MIT and GPL version 2.0 licenses. This means that you can +choose the license that best suits your project and use it accordingly. + +Although not required, the author would appreciate an email letting him +know of any substantial use of jqPlot. You can reach the author at: +chris at jqplot or see http://www.jqplot.com/info.php . + +If you are feeling kind and generous, consider supporting the project by +making a donation at: http://www.jqplot.com/donate.php . + +jqPlot includes date instance methods and printf/sprintf functions by other authors: + +Date instance methods: + + author Ken Snyder (ken d snyder at gmail dot com) + date 2008-09-10 + version 2.0.2 (http://kendsnyder.com/sandbox/date/) + license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) + +JavaScript printf/sprintf functions. + + version 2007.04.27 + author Ash Searle + http://hexmen.com/blog/2007/03/printf-sprintf/ + http://hexmen.com/js/sprintf.js + The author (Ash Searle) has placed this code in the public domain: + "This code is unrestricted: you are free to use it however you like." diff --git a/web/extensions/jqplot/changes.txt b/web/extensions/jqplot/changes.txt new file mode 100644 index 0000000..ce990ea --- /dev/null +++ b/web/extensions/jqplot/changes.txt @@ -0,0 +1,458 @@ +Title: Change Log + +1.0.8: +* Issue #375: sortMergedLabels does not sort string labels +* Issue #279: Groups > 3 Causes Alignment Issues +* Issue #439: IE can't display a customized legend in Quirks mode +* Issue #482: "Undefined" error message when plotting a chart with no data +* Issue #116: Don't mix spaces and tabs for indentation +* Issue #564: Metergauge renderer not resizable when replotting +* Issue #409: MeterGaugeRenderer replot/redraw offsets center +* Issue #523: Adding rectangles to Canvas Overlay plugin +* Issue #756: jqplot.min files contain non-UTF-8 characters +* Issue #223: fillToZero does not color negative values when crossover point is 0 +* Pull Request #23: Adding rectangles to Canvas Overlay plugin +* Pull Request #28: Cross-over points of 0 will actually change colors +* Pull Request #35: Don't highlight hidden bars or show tooltips for them +* Pull Request #41: Add dutch(nl) and svenska(sv) translations for dates +* Add tooltip support for Pie Charts +* Update to latest YUI compressor + +1.0.7: +* Issue #726: Bug in sprintf %p, sometimes it outputs exponential form rather than decimal +* Issue #717: Plot's preDrawHooks not called +* Issue #707: Browser hangs with LogAxisRenderer when value is 0 +* Issue #695: Horizontal Bar Chart Negative Series Colors Not Working +* Issue #670: Examples IE7, IE8 and IE9 multipleBarColors.html failure and fix +* Issue #636: X Axis Date Renderer Single Day Not plotting +* Issue #607: Integration issue +* Issue #571: Decimal numbers not properly formatted +* Issue #552: jqPlot crashes when interval too small +* Issue #536: DateAxisRenderer invalid scaling +* Issue #534: "decimalMark" in the "jqplot.sprintf.js" +* Issue #529: Scientific notation on label values ending in 0 +* Issue #521: invalid JS in meterGaugeRenderer.js +* Issue #516: Including BezierCurveRenderer plugin and initializing jqplot with no options give error +* Issue #500: DateAxisRenderer has timezone related issues +* Issue #452: Including ALL jqPlot plugins causes an Error +* Issue #494: No point when use LogAxisRenderer and a point has a zero value +* Issue #430: getIsoWeek: invalid method call +* Issue #280: jqplot Options +* Issue #179: Spelling/grammar +* Pull Request #18: Implement getTop in CanvasAxisTickRenderer +* Pull Request #21: Performance issue when drawing pointlabels with zeros/null values +* Pull Request #24: Added suggested fix in comment #8 for issue #536 +* Pull Request #29: Removed unbalanced addition of UTC offset +* Pull Request #33: Documentation fixes (issue #179, other changes) +* Pull Request #34: Start of updating jqPlotOptions.txt +* Pull Request #37: Example and suggested fix for issues #552 and issue #536 +* Pull Request #39: Fixed trailing comma which caused issues with IE7 + +1.0.6: +* Add left sidebar navigation to examples +* Update examples for jquery 1.9.1 and jquery ui 1.10.0 +* Add colorpicker.js to distribution +* Fix some problems with examples when viewing with local file system +* Add "minified" copyright notice for minified files, similar to jquery's notice. +* Pull Request #25: jqplot.sprintf.js is no longer the last file in the concatenated jquery.jqplot.js +* Pull Request #17: Fixed bug causing custom pointLabels passed with plot data to be ignored for horizontal bar graphs. +* Pull Request #10: Build error by invalid encoding. +* Issue #714: handle tickColor in meterGaugeRenderer +* Issue #519: jsDate Polish Localization + +1.0.5: +* Updated to jQuery 1.9 + +1.0.0b2: +* Major improvements in memory usage: +** Merged in changes from Timo Besenruether to reuse canvas elements and improve + memory performance. +** Fixed all identifiable DOM leaks. +** Mergged in changes from cguillot for memory improvements in IE < 9. +* Added vertical and dashed vertical line support for canvas overlay. +* Fixed bug where initially hidden plots would not display. +* Fixed bug with point labels and null data points. +* Updated to jQuery 1.6.1. +* Improved pie slice margin calculation and fixed slice margin and pie positioning + with small slices. +* Improved bar renderer so bars always start at 0 if: +** The axis is a linear axis (not log/date). +** There are no other line types besides bars attached to the axis. +** The data on the axis is all >= 0. +** The user has not specified a pad, padMin or forceTickAt0 = true option. +* Modified tick prefix behavious so prefix no added to all ticks, even if format + string is specified. +* Fix to ensure original tick formats are applied when zooming and resetting + zoom. +* Updated auto tick format string so format adjusted when zooming. +* Modified auto tick computation to put less ticks on small plots and more + ticks on large plots. +* Update bubble render to support gradients in IE 9. + +1.0.0b1: +* Much improved tick generation algorithm to get precise rounded + tick values (Thanks Scott Prahl!). +* Auto compute tick format string if none is provided. +* Much better "slicing" of pie charts when using "sliceMargin" option to set + a gap between the slices. +* Expanded canvasOverlay plugin to create arbitrary dashed and solid + horizontal and vertical lines on top of plot. +* Added defaultColors and defaultNegativeColors options to $.jqplot.config. +* Fixed issue #318, highlighter & bar renderer incompatability. +* Improve highlighter tooltip positioning with negative bars. +* Fixed #305, mispelling of jqlotDragStart and jqlotDragStop. MUST NOW BIND + TO jqplotDragStart and jqplotDragStop. +* Fixed #290, some variables left in global scope. +* Fixed #289, OHLC line widths hard coded at 1.5. Now set by lineWidth option. +* Fixed #296 for determining databounds on log axes. +* Updated to jQuery 1.5.1 +* Fixed waterfall plot to ensure first and last bars always fill to zero. +* Added lineJoin and lineCap option to series lines. +* Bar widths now based on width of grid, not plot target for better scaling. +* Added looseZoom option to cursor so zooming can produce well rounded ticks. +* Added forceTickAt0 and forceTickAt100 options to ensure there will always + be a tick at 0 or 100 in the plot. +* Fixed bug where cursor legend didn't honor series showLabel option. + + +1.0.0a: + +* Series can now be moved forward or backward in stack to e.g. bring a line + forward when mousing over a point. +* Can now move outside of grid area while zooming. Can have zoom + constrained to grid area or allow zooming outside. +* Fixed issue #142 with tooltip drawn on top of event canvas, hiding + mouse events. +* Fixed #147 where pie slices with 0 value not rendering properly in IE. +* Fixed #130 where stack data not sorted properly. +* Fixed bug with null values not handled properly in category axes. +* Fixed #156 where pie charts not rendering on QTWebKit. +* Now using feature detection for canvas and canvas text capability + rather than browser version. +* Added enahncedLegendRenderer plugin to allow multi row/column legends + and clickable labels to show/hide series. +* Added fillToValue option to allow filled line plot to fill to an + arbitrary value. +* Added block plot plugin. +* Added funnel type charts. +* Added meter gauge type charts. +* Added plot theming support. +* $.jqplot.config.enablePlugins now false by default. +* Implemented highlighting on bar, pie, donut, funnel, etc. charts. +* Fix to pointlabels plugin to align labels properly on multi series plots. +* Added custom error handling to display error message in plot area. +* Fixed issue where would call to draw grid border of 0 width would + result in a default border being drawn. +* Added options to place legend outside of grid and shrink grid so everything + stays within plot div. +* Fixed bug in color generator so now calls to get() continually cycle + through colors just like next(). +* Added defaultAxisStart option. +* Added gradient fills to bubbles. +* Added bubble charts. +* Added showLabels option to bubble charts. +* Pass bubble radius to event callback in bubble charts. +* Fixed #207, typo in docs. +* Fixed #206 where "value" pie slice data labels were displaying wrong + value. +* Fixed #147 with 0 value slices in IE6. +* Fixed issue #241, disabled varyBarColor option in stacked charts. +* Added dataRenderer option to allow custom processors for JSON, AJAX + and anywhere else you might want to get data. +* Fixed null value handling so plot now properly skip or join over nulls. +* Fixed showTicks and showTickMarks option conflicts. +* Fixed issue #185 where pointLabels plugin incompatibility could crash + pie, donut and other plots. +* Fixed #23 and #143 to obey gridPadding option. +* Fixed #233 with highlighter tooltip separator. +* Fixed #224 where type checking failing on GWT. +* Fixed #272 with pie highlighting not working on replot. +* Memory performance improvements. +* Changes to build script so everything should build when pulled from repo. +* Fixed issue #275, IE 6/7 don't support array indexing of strings. +* Added event listener hooks for mouseUp, mouseDown, etc. to all line plots. +* Fixed bug with highlighter not working when null in data. +* Updated to jQuery 1.4.4 +* Fixed bug where donut plots showed value of radians of slice instead + of actual data. +* Reverted to excanvas r3 so IE8 no longer has to emulate IE7. +* Added tooltipContentEditor option to highlighter, allowing callback + to manipulate tooltip content at run time (thanks Tim Bunce!). +* Fixed bug where axes scale not resetting. +* Fixed bug with date axes where data bounds not properly set. +* Fixed issue where tick marks disappear if grid lines turned off. +* Updated replot method to allow passing in axes options for more control. +* Added experimental support for "broken" axes. +* Fixed bug with pies where pies with 0 valued slices did not draw correctly. +* Added canvasOverlay plugin to allow drawing of arbitrary shapes on a canvas + over the plot. +* Added option to display arbitrary text/html (message, animated gif, etc.) if + plot is constructed without data. Allow a "data loading" indicator to be shown. +* Added resetAxisValues method to manually update axis ticks without + redrawing the plot. +* Fix to labels on negative bars so label postiion of 'n' will be below a negative bar, + just as it is above a positive bar (thanks guigod!). +* Added thousands separator character (') to sprintf formatting (thanks yuichi1004!). +* Re-factored date parsing/formatting to use new jsDate module which does not + extend the Date prototype. + + +0.9.7: + +* Added Mekko chart plot type with enhanced legend and axes support. +* Implemented vertical waterfall charts. Can create waterfall plot as + option to bar chart. See examples folder of distribution. +* Enhanced plot labels for waterfall style. +* Enhanced bar plots so you can now color each bar of a series + independently with the "varyBarColor" option. +* Re-factored series drawing so that each series and series shadow drawn + on its own canvas. Allows series to be redrawn independently of each other. +* Added additional default series colors. +* Added useNegativeColors option to turn off negative color array and use + only seriesColors array to define all bar/filled line colors. +* Fix css for cursor legend. +* Modified shape renderer so rectangles can be stroked and filled. +* Re-factored date methods out of dateAxisRenderer so that date formatter + and methods can be accesses outside of dateAxisRenderer plugin. +* Fixed #132, now trigger series change event on plot target instead of drag canvas. +* Fixes issue #116 where some source files had mix of tabs and spaces + for indentation. Should have been all spaces. +* Fixed issue #126, some links broken in docs section of web site. +* Fixed issue #90, trendline plugin incompatibility with pie renderer. +* Updated samples in examples folder of distribution to include navigation + links if web server is set up to process .html files with php. + + +0.9.6: + +* New, easier to use, replot() method for placing plots in tabs, accordions, + resizable containers or for changing plot parameters programmatically. +* Updated legend renderer for pie charts to draw swatches which will + print correctly. +* Fixed issue #118 with patch from taum so autoscale option will + honor tickInterval and numberTicks options +* Fix to plot diameter calculation for initially hidden plots. +* Added examples for making plots in jQuery UI tabs and accordions. +* Fixed issue #120 where pie chart with single slice not displaying + correctly in IE and Chrome + + +0.9.5.2: + +* Fixed #102 where double clicking on plot that has zoom enabled, but + has not been zoomed resulted in error. +* Fixed bug where candlestick coloring options not working. +* Added option to turn individual series labels off in the legend. + + +0.9.5.1: + +* Fixed bug where tooltip not working with OHLC and candlestick charts. +* Added additional marker styles: plus, X and dash. + + +0.9.5: + +* Implemented "zoomProxy". zoomProxy allows zooming one plot from another + such as an overview plot. +* Zooming can now be constrained to just x or y axis. +* Enhanced cursor plugin with vertical "dataTracking" line. This is a line + at the cursor location with a readout of data points at the line location + which are displayed in the chart legend. +* Changed cursor tooltip format string. Now one format string is used for + entire tooltip. +* Added mechanisms to specify plot size when plot target is hidden or plot + height/width otherwise cannot be determined from markup. +* Added $.jqplot.config object to specify jqplot wide configuration options. + These include enablePlugins to globally set the default plugin state on/off + and defaultHeight/defaultWidth to specify default plot height/width. +* Added fillToZero option which forces filled charts to fill to zero as opposed + to axis minimum. Thus negative filled bar/line values will fill upwards to + zero axis value. +* Added option to disable stacking on individual lines. +* Changed targetId property of the plot object so it now includes a "#" before + the id string. +* Improved tick and body sizing of Open Hi Low Close and candlestick charts. +* Removed lots of web site related files from the repository. This means that, + if working from the sources, user's won't be able to build the jqplot web + site and the docs/tests that are hosted on that site. The minified and + compressed distribution packages will build fine. +* Lots of examples were added to a separate examples directory to better show + functionality of jqPlot for local testing with the distribution. +* Many various bug fixes and other minor enhancements. + + +0.9.4: + +* Implemented axis labels. Labels can be rendered in div tags or as canvas + elements supporting rotated text. +* Improved rotated axis label positioning so labels will start or end at a + tick position. +* Fixed bug where an empty data series would hang plot rendering. +* completed issue #66 for misc. improvements to documentation. +* Fixed issue #64 where the same ID's were assigned to cursor and highlighter + elements. +* Added option to legend to encode special HTML characters. +* Fixed undesirable behavior where point labels for points off the plot + were being rendered. +* Added edgeTolerance option to point label renderer to control rendering of + labels near plot edges. + + +0.9.3: + +* Preliminary support for axis labels. Currently rendered into DIV tags, + so no rotated label support. This feature is currently experimental. +* Fixed bug #52, needed space in tick div tag between style and class declarations + or plot failed in certain application doctypes. +* Fixed issue #54, miter style line join for chart lines causing spikes at steep + changes in slope. Changed miter style to round. +* Added examples for new autoscaling algorithm. +* Fixed bug #57, category axis labels disappear on redraw() +* Improved algorithm which controlled maximum number of labels that would display + on a category axis. +* Fixed bug #45 where null values causing errors in plotData and gridData. +* Fixed issue #60 where seriesColors option was not working. + + +0.9.2: + +* Fixed bug #45 where a plot could crash if series had different numbers of points. +* Fixed issue #50, added option to turn off sorting of series data. +* Fixed issue #31, implemented a better axis autoscaling algorithm and added an autoscale option. + +0.9.1: + +* Fixed bug #40, when axis pad, padMax, padMin set to 0, graph would fail to render. +* Fixed bug #41 where pie and bar charts not rendered correctly on redraw(). +* Fixed bug #11, filled stacked line plots not rendering correctly in IE. +* Fixed bug #42 where stacked charts not rendering with string date axis ticks. +* Fixed bug in redraw() method where axes ticks were not reset. +* Fixed "jqplotPreRedrawEvent" that should have been named "jqplotPostRedraw" event. + +0.9.0: + +* Added Open Hi Low Close charts, Candlestick charts and Hi Low Close charts. +* Added support for arbitrary labels on the data points. +* Enhanced highlighter plugin to allow custom formatting control of entire tooltip. +* Enhanced highlighter to support multiple y values in a data point. +* Fixed bug #38 where series with a single point with a negative value would fail. +* Improvements to examples to show what plugins to include. +* Expanded documentation for some of the plugins. + +0.8.5: + +* Added zooming ability with double click or single click options to reset zoom. +* Modified default tick spacing algorithm for date axes to give more space to ticks. +* Fixed bug #2 where tickInterval wasn't working properly. +* Added neighborThreshold option to control how close mouse must be to + point to trigger neighbor detection. +* Added double click event handler on plot. + +0.8.0: + +* Support for up to 9 y axes. +* Added option to control padding at max/min bounds of axes separately. +* Closed issue #21, added options to control grid line color and width. +* Closed issue #20, added options to filled line charts to stoke above + fill and customize fill color and transparency. +* Improved structure of on line documentation to make usage and options + docs default. +* Added much documentation on options and css styling. + +0.7.1: + +* Bug fix release +* Fixed bug #6, missing semi-colons messing up some javascript compressors. +* Fixed bug #13 where 2D ticks array of [values, labels] would fail to + renderer with DateAxisRenderer. +* Fixes bug #16 where pie renderer overwriting options for all plot types + and crashing non pie plots. +* Fixes bug #17 constrainTo dragable option mispelled as "contstrainTo". + Fixed dragable color issue when used with trend lines. + +0.7.0: + +* Pie chart support +* Enabled tooltipLocation option in highlighter. +* Highlighter Tooltip will account for mark size and highlight size when + positioning itself. +* Added ability to show just x, y or both axes in highlighter tooltip. +* Added customization of separator between axes values in highlighter tooltip. +* Modified how shadows are drawn for lines, bars and markers. Now drawn first, + so they are always behind the object. +* Adjustments to shadow parameters on lines to account for new shadow positioning. +* Added a ColorGenerator class to robustly return next available color + for a plot with wrap around to first color at end. +* Udates to docs about css file. +* Fixed bug with String x values in series and IE error on sorting (Category Axis). +* Added cursor changes in dragable plugin when cursor near dragable point. + +0.6.6b: + +* Added excanvas.js and excanvas.min.js to compressed distributions. +* Added example/test html pages I had locally into repository and to + compressed distributions. + +0.6.6a: + +* Removed absolute positioning from dom element and put back into css file. +* Duplicate of 0.6.6 with a suffix to unambiguously differentiate between + previously posted 0.6.6 release. + +0.6.6: + +* Fixed bug #5, trend line plugin failing when no trend line options specified. +* Added absolute position css spec to axis tick dom element. +* Enhancement to category axes, more intuitive handling of series with + missing data values. + +0.6.5: + +* Fixed bug #4, series of unequal data length not rendering correctly. + This is a bugfix release only. + +0.6.4: + +* Fixed bug (issue #1 in tracker) where flat line data series (all x and/or y + values are euqal) or single value data series would crash. + +0.6.3: + +* Support for stacked line (a.k.a. area) and stacked bar (horizontal and + vertical) charts. +* Refactored barRenderer to use default shape and shadow renderers. +* Added info (contacts & support information) page to web site. + +0.6.2: + +* This is a minor upgrade to docs and build only. No functionality has changed. +* Ant build script generates entire site, examples, tests and distribution. +* Improvements to documentation. + +0.6.1: + +* New sprintf implementation from Ash Searle that implements %g. +* Fix to sprintf e/f formats. +* Created new format specifier, %p and %P to preserve significance. +* Modified p/P format to better display larger numbers. +* Fixed and simplified significant digits calculation for sprintf. +* Added option to have cursor tooltip follow the mouse or not. +* Added options to change size of highlight. +* Updates to handle dates like '6-May-09'. +* Mods to improve look of web site. +* Updates to documentation. +* Added license and copyright statement to source files. + +0.6.0: + +* Added rotated text support. Uses native canvas text functionality in + browsers that support it or draws text on canvas with Hershey font +* metrics for non-supporting browsers. +* Removed lots of lint in js code. +* Moved tick css from js code into css file. +* Fix to tick positioning css. y axis ticks were positioned to wrong side of axis div. +* Re-factored axis tick renderer instantiation into the axes renderers themselves. + + +For changes prior to 0.6.0 release, please see change log at http://bitbucket.org/cleonello/jqplot/changesets/ diff --git a/web/extensions/jqplot/copyright.txt b/web/extensions/jqplot/copyright.txt new file mode 100644 index 0000000..86d4c40 --- /dev/null +++ b/web/extensions/jqplot/copyright.txt @@ -0,0 +1,56 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: @VERSION + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + * included jsDate library by Chris Leonello: + * + * Copyright (c) 2010-2013 Chris Leonello + * + * jsDate is currently available for use in all personal or commercial projects + * under both the MIT and GPL version 2.0 licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * jsDate borrows many concepts and ideas from the Date Instance + * Methods by Ken Snyder along with some parts of Ken's actual code. + * + * Ken's origianl Date Instance Methods and copyright notice: + * + * Ken Snyder (ken d snyder at gmail dot com) + * 2008-09-10 + * version 2.0.2 (http://kendsnyder.com/sandbox/date/) + * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) + * + * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js. + * Larry has generously given permission to adapt his code for inclusion + * into jqPlot. + * + * Larry's original code can be found here: + * + * https://github.com/lsiden/export-jqplot-to-png + * + * + */ diff --git a/web/extensions/jqplot/excanvas.js b/web/extensions/jqplot/excanvas.js new file mode 100644 index 0000000..4ca9653 --- /dev/null +++ b/web/extensions/jqplot/excanvas.js @@ -0,0 +1,1438 @@ +// Memory Leaks patch from http://explorercanvas.googlecode.com/svn/trunk/ +// svn : r73 +// ------------------------------------------------------------------ +// Copyright 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +// Known Issues: +// +// * Patterns only support repeat. +// * Radial gradient are not implemented. The VML version of these look very +// different from the canvas one. +// * Clipping paths are not implemented. +// * Coordsize. The width and height attribute have higher priority than the +// width and height style values which isn't correct. +// * Painting mode isn't implemented. +// * Canvas width/height should is using content-box by default. IE in +// Quirks mode will draw the canvas using border-box. Either change your +// doctype to HTML5 +// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) +// or use Box Sizing Behavior from WebFX +// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) +// * Non uniform scaling does not correctly scale strokes. +// * Optimize. There is always room for speed improvements. + +// Only add this code if we do not already have a canvas implementation +if (!document.createElement('canvas').getContext) { + +(function() { + + // alias some functions to make (compiled) code shorter + var m = Math; + var mr = m.round; + var ms = m.sin; + var mc = m.cos; + var abs = m.abs; + var sqrt = m.sqrt; + + // this is used for sub pixel precision + var Z = 10; + var Z2 = Z / 2; + + var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; + + /** + * This funtion is assigned to the elements as element.getContext(). + * @this {HTMLElement} + * @return {CanvasRenderingContext2D_} + */ + function getContext() { + return this.context_ || + (this.context_ = new CanvasRenderingContext2D_(this)); + } + + var slice = Array.prototype.slice; + + /** + * Binds a function to an object. The returned function will always use the + * passed in {@code obj} as {@code this}. + * + * Example: + * + * g = bind(f, obj, a, b) + * g(c, d) // will do f.call(obj, a, b, c, d) + * + * @param {Function} f The function to bind the object to + * @param {Object} obj The object that should act as this when the function + * is called + * @param {*} var_args Rest arguments that will be used as the initial + * arguments when the function is called + * @return {Function} A new function that has bound this + */ + function bind(f, obj, var_args) { + var a = slice.call(arguments, 2); + return function() { + return f.apply(obj, a.concat(slice.call(arguments))); + }; + } + + function encodeHtmlAttribute(s) { + return String(s).replace(/&/g, '&').replace(/"/g, '"'); + } + + function addNamespace(doc, prefix, urn) { + if (!doc.namespaces[prefix]) { + doc.namespaces.add(prefix, urn, '#default#VML'); + } + } + + function addNamespacesAndStylesheet(doc) { + addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml'); + addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office'); + + // Setup default CSS. Only add one style sheet per document + if (!doc.styleSheets['ex_canvas_']) { + var ss = doc.createStyleSheet(); + ss.owningElement.id = 'ex_canvas_'; + ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + + // default size is 300x150 in Gecko and Opera + 'text-align:left;width:300px;height:150px}'; + } + } + + // Add namespaces and stylesheet at startup. + addNamespacesAndStylesheet(document); + + var G_vmlCanvasManager_ = { + init: function(opt_doc) { + var doc = opt_doc || document; + // Create a dummy element so that IE will allow canvas elements to be + // recognized. + doc.createElement('canvas'); + doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); + }, + + init_: function(doc) { + // find all canvas elements + var els = doc.getElementsByTagName('canvas'); + for (var i = 0; i < els.length; i++) { + this.initElement(els[i]); + } + }, + + /** + * Public initializes a canvas element so that it can be used as canvas + * element from now on. This is called automatically before the page is + * loaded but if you are creating elements using createElement you need to + * make sure this is called on the element. + * @param {HTMLElement} el The canvas element to initialize. + * @return {HTMLElement} the element that was created. + */ + initElement: function(el) { + if (!el.getContext) { + el.getContext = getContext; + + // Add namespaces and stylesheet to document of the element. + addNamespacesAndStylesheet(el.ownerDocument); + + // Remove fallback content. There is no way to hide text nodes so we + // just remove all childNodes. We could hide all elements and remove + // text nodes but who really cares about the fallback content. + el.innerHTML = ''; + + // do not use inline function because that will leak memory + el.attachEvent('onpropertychange', onPropertyChange); + el.attachEvent('onresize', onResize); + + var attrs = el.attributes; + if (attrs.width && attrs.width.specified) { + // TODO: use runtimeStyle and coordsize + // el.getContext().setWidth_(attrs.width.nodeValue); + el.style.width = attrs.width.nodeValue + 'px'; + } else { + el.width = el.clientWidth; + } + if (attrs.height && attrs.height.specified) { + // TODO: use runtimeStyle and coordsize + // el.getContext().setHeight_(attrs.height.nodeValue); + el.style.height = attrs.height.nodeValue + 'px'; + } else { + el.height = el.clientHeight; + } + //el.getContext().setCoordsize_() + } + return el; + }, + + // Memory Leaks patch : see http://code.google.com/p/explorercanvas/issues/detail?id=82 + uninitElement: function(el){ + if (el.getContext) { + var ctx = el.getContext(); + delete ctx.element_; + delete ctx.canvas; + el.innerHTML = ""; + //el.outerHTML = ""; + el.context_ = null; + el.getContext = null; + el.detachEvent("onpropertychange", onPropertyChange); + el.detachEvent("onresize", onResize); + } + } + }; + + function onPropertyChange(e) { + var el = e.srcElement; + + switch (e.propertyName) { + case 'width': + el.getContext().clearRect(); + el.style.width = el.attributes.width.nodeValue + 'px'; + // In IE8 this does not trigger onresize. + el.firstChild.style.width = el.clientWidth + 'px'; + break; + case 'height': + el.getContext().clearRect(); + el.style.height = el.attributes.height.nodeValue + 'px'; + el.firstChild.style.height = el.clientHeight + 'px'; + break; + } + } + + function onResize(e) { + var el = e.srcElement; + if (el.firstChild) { + el.firstChild.style.width = el.clientWidth + 'px'; + el.firstChild.style.height = el.clientHeight + 'px'; + } + } + + G_vmlCanvasManager_.init(); + + // precompute "00" to "FF" + var decToHex = []; + for (var i = 0; i < 16; i++) { + for (var j = 0; j < 16; j++) { + decToHex[i * 16 + j] = i.toString(16) + j.toString(16); + } + } + + function createMatrixIdentity() { + return [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1] + ]; + } + + function matrixMultiply(m1, m2) { + var result = createMatrixIdentity(); + + for (var x = 0; x < 3; x++) { + for (var y = 0; y < 3; y++) { + var sum = 0; + + for (var z = 0; z < 3; z++) { + sum += m1[x][z] * m2[z][y]; + } + + result[x][y] = sum; + } + } + return result; + } + + function copyState(o1, o2) { + o2.fillStyle = o1.fillStyle; + o2.lineCap = o1.lineCap; + o2.lineJoin = o1.lineJoin; + o2.lineWidth = o1.lineWidth; + o2.miterLimit = o1.miterLimit; + o2.shadowBlur = o1.shadowBlur; + o2.shadowColor = o1.shadowColor; + o2.shadowOffsetX = o1.shadowOffsetX; + o2.shadowOffsetY = o1.shadowOffsetY; + o2.strokeStyle = o1.strokeStyle; + o2.globalAlpha = o1.globalAlpha; + o2.font = o1.font; + o2.textAlign = o1.textAlign; + o2.textBaseline = o1.textBaseline; + o2.arcScaleX_ = o1.arcScaleX_; + o2.arcScaleY_ = o1.arcScaleY_; + o2.lineScale_ = o1.lineScale_; + } + + var colorData = { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + grey: '#808080', + greenyellow: '#ADFF2F', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + oldlace: '#FDF5E6', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + whitesmoke: '#F5F5F5', + yellowgreen: '#9ACD32' + }; + + + function getRgbHslContent(styleString) { + var start = styleString.indexOf('(', 3); + var end = styleString.indexOf(')', start + 1); + var parts = styleString.substring(start + 1, end).split(','); + // add alpha if needed + if (parts.length != 4 || styleString.charAt(3) != 'a') { + parts[3] = 1; + } + return parts; + } + + function percent(s) { + return parseFloat(s) / 100; + } + + function clamp(v, min, max) { + return Math.min(max, Math.max(min, v)); + } + + function hslToRgb(parts){ + var r, g, b, h, s, l; + h = parseFloat(parts[0]) / 360 % 360; + if (h < 0) + h++; + s = clamp(percent(parts[1]), 0, 1); + l = clamp(percent(parts[2]), 0, 1); + if (s == 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hueToRgb(p, q, h + 1 / 3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1 / 3); + } + + return '#' + decToHex[Math.floor(r * 255)] + + decToHex[Math.floor(g * 255)] + + decToHex[Math.floor(b * 255)]; + } + + function hueToRgb(m1, m2, h) { + if (h < 0) + h++; + if (h > 1) + h--; + + if (6 * h < 1) + return m1 + (m2 - m1) * 6 * h; + else if (2 * h < 1) + return m2; + else if (3 * h < 2) + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + else + return m1; + } + + var processStyleCache = {}; + + function processStyle(styleString) { + if (styleString in processStyleCache) { + return processStyleCache[styleString]; + } + + var str, alpha = 1; + + styleString = String(styleString); + if (styleString.charAt(0) == '#') { + str = styleString; + } else if (/^rgb/.test(styleString)) { + var parts = getRgbHslContent(styleString); + var str = '#', n; + for (var i = 0; i < 3; i++) { + if (parts[i].indexOf('%') != -1) { + n = Math.floor(percent(parts[i]) * 255); + } else { + n = +parts[i]; + } + str += decToHex[clamp(n, 0, 255)]; + } + alpha = +parts[3]; + } else if (/^hsl/.test(styleString)) { + var parts = getRgbHslContent(styleString); + str = hslToRgb(parts); + alpha = parts[3]; + } else { + str = colorData[styleString] || styleString; + } + return processStyleCache[styleString] = {color: str, alpha: alpha}; + } + + var DEFAULT_STYLE = { + style: 'normal', + variant: 'normal', + weight: 'normal', + size: 10, + family: 'sans-serif' + }; + + // Internal text style cache + var fontStyleCache = {}; + + function processFontStyle(styleString) { + if (fontStyleCache[styleString]) { + return fontStyleCache[styleString]; + } + + var el = document.createElement('div'); + var style = el.style; + try { + style.font = styleString; + } catch (ex) { + // Ignore failures to set to invalid font. + } + + return fontStyleCache[styleString] = { + style: style.fontStyle || DEFAULT_STYLE.style, + variant: style.fontVariant || DEFAULT_STYLE.variant, + weight: style.fontWeight || DEFAULT_STYLE.weight, + size: style.fontSize || DEFAULT_STYLE.size, + family: style.fontFamily || DEFAULT_STYLE.family + }; + } + + function getComputedStyle(style, element) { + var computedStyle = {}; + + for (var p in style) { + computedStyle[p] = style[p]; + } + + // Compute the size + var canvasFontSize = parseFloat(element.currentStyle.fontSize), + fontSize = parseFloat(style.size); + + if (typeof style.size == 'number') { + computedStyle.size = style.size; + } else if (style.size.indexOf('px') != -1) { + computedStyle.size = fontSize; + } else if (style.size.indexOf('em') != -1) { + computedStyle.size = canvasFontSize * fontSize; + } else if(style.size.indexOf('%') != -1) { + computedStyle.size = (canvasFontSize / 100) * fontSize; + } else if (style.size.indexOf('pt') != -1) { + computedStyle.size = fontSize / .75; + } else { + computedStyle.size = canvasFontSize; + } + + // Different scaling between normal text and VML text. This was found using + // trial and error to get the same size as non VML text. + computedStyle.size *= 0.981; + + // Fix for VML handling of bare font family names. Add a '' around font family names. + computedStyle.family = "'" + computedStyle.family.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'"; + + return computedStyle; + } + + function buildStyle(style) { + return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + + style.size + 'px ' + style.family; + } + + var lineCapMap = { + 'butt': 'flat', + 'round': 'round' + }; + + function processLineCap(lineCap) { + return lineCapMap[lineCap] || 'square'; + } + + /** + * This class implements CanvasRenderingContext2D interface as described by + * the WHATWG. + * @param {HTMLElement} canvasElement The element that the 2D context should + * be associated with + */ + function CanvasRenderingContext2D_(canvasElement) { + this.m_ = createMatrixIdentity(); + + this.mStack_ = []; + this.aStack_ = []; + this.currentPath_ = []; + + // Canvas context properties + this.strokeStyle = '#000'; + this.fillStyle = '#000'; + + this.lineWidth = 1; + this.lineJoin = 'miter'; + this.lineCap = 'butt'; + this.miterLimit = Z * 1; + this.globalAlpha = 1; + this.font = '10px sans-serif'; + this.textAlign = 'left'; + this.textBaseline = 'alphabetic'; + this.canvas = canvasElement; + + var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' + + canvasElement.clientHeight + 'px;overflow:hidden;position:absolute'; + var el = canvasElement.ownerDocument.createElement('div'); + el.style.cssText = cssText; + canvasElement.appendChild(el); + + var overlayEl = el.cloneNode(false); + // Use a non transparent background. + overlayEl.style.backgroundColor = 'red'; + overlayEl.style.filter = 'alpha(opacity=0)'; + canvasElement.appendChild(overlayEl); + + this.element_ = el; + this.arcScaleX_ = 1; + this.arcScaleY_ = 1; + this.lineScale_ = 1; + } + + var contextPrototype = CanvasRenderingContext2D_.prototype; + contextPrototype.clearRect = function() { + if (this.textMeasureEl_) { + this.textMeasureEl_.removeNode(true); + this.textMeasureEl_ = null; + } + this.element_.innerHTML = ''; + }; + + contextPrototype.beginPath = function() { + // TODO: Branch current matrix so that save/restore has no effect + // as per safari docs. + this.currentPath_ = []; + }; + + contextPrototype.moveTo = function(aX, aY) { + var p = getCoords(this, aX, aY); + this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); + this.currentX_ = p.x; + this.currentY_ = p.y; + }; + + contextPrototype.lineTo = function(aX, aY) { + var p = getCoords(this, aX, aY); + this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); + + this.currentX_ = p.x; + this.currentY_ = p.y; + }; + + contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY) { + var p = getCoords(this, aX, aY); + var cp1 = getCoords(this, aCP1x, aCP1y); + var cp2 = getCoords(this, aCP2x, aCP2y); + bezierCurveTo(this, cp1, cp2, p); + }; + + // Helper function that takes the already fixed cordinates. + function bezierCurveTo(self, cp1, cp2, p) { + self.currentPath_.push({ + type: 'bezierCurveTo', + cp1x: cp1.x, + cp1y: cp1.y, + cp2x: cp2.x, + cp2y: cp2.y, + x: p.x, + y: p.y + }); + self.currentX_ = p.x; + self.currentY_ = p.y; + } + + contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { + // the following is lifted almost directly from + // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes + + var cp = getCoords(this, aCPx, aCPy); + var p = getCoords(this, aX, aY); + + var cp1 = { + x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), + y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) + }; + var cp2 = { + x: cp1.x + (p.x - this.currentX_) / 3.0, + y: cp1.y + (p.y - this.currentY_) / 3.0 + }; + + bezierCurveTo(this, cp1, cp2, p); + }; + + contextPrototype.arc = function(aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise) { + aRadius *= Z; + var arcType = aClockwise ? 'at' : 'wa'; + + var xStart = aX + mc(aStartAngle) * aRadius - Z2; + var yStart = aY + ms(aStartAngle) * aRadius - Z2; + + var xEnd = aX + mc(aEndAngle) * aRadius - Z2; + var yEnd = aY + ms(aEndAngle) * aRadius - Z2; + + // IE won't render arches drawn counter clockwise if xStart == xEnd. + if (xStart == xEnd && !aClockwise) { + xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something + // that can be represented in binary + } + + var p = getCoords(this, aX, aY); + var pStart = getCoords(this, xStart, yStart); + var pEnd = getCoords(this, xEnd, yEnd); + + this.currentPath_.push({type: arcType, + x: p.x, + y: p.y, + radius: aRadius, + xStart: pStart.x, + yStart: pStart.y, + xEnd: pEnd.x, + yEnd: pEnd.y}); + + }; + + contextPrototype.rect = function(aX, aY, aWidth, aHeight) { + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + }; + + contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { + var oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.stroke(); + + this.currentPath_ = oldPath; + }; + + contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { + var oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.fill(); + + this.currentPath_ = oldPath; + }; + + contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { + var gradient = new CanvasGradient_('gradient'); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + return gradient; + }; + + contextPrototype.createRadialGradient = function(aX0, aY0, aR0, + aX1, aY1, aR1) { + var gradient = new CanvasGradient_('gradientradial'); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.r0_ = aR0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + gradient.r1_ = aR1; + return gradient; + }; + + contextPrototype.drawImage = function(image, var_args) { + var dx, dy, dw, dh, sx, sy, sw, sh; + + // to find the original width we overide the width and height + var oldRuntimeWidth = image.runtimeStyle.width; + var oldRuntimeHeight = image.runtimeStyle.height; + image.runtimeStyle.width = 'auto'; + image.runtimeStyle.height = 'auto'; + + // get the original size + var w = image.width; + var h = image.height; + + // and remove overides + image.runtimeStyle.width = oldRuntimeWidth; + image.runtimeStyle.height = oldRuntimeHeight; + + if (arguments.length == 3) { + dx = arguments[1]; + dy = arguments[2]; + sx = sy = 0; + sw = dw = w; + sh = dh = h; + } else if (arguments.length == 5) { + dx = arguments[1]; + dy = arguments[2]; + dw = arguments[3]; + dh = arguments[4]; + sx = sy = 0; + sw = w; + sh = h; + } else if (arguments.length == 9) { + sx = arguments[1]; + sy = arguments[2]; + sw = arguments[3]; + sh = arguments[4]; + dx = arguments[5]; + dy = arguments[6]; + dw = arguments[7]; + dh = arguments[8]; + } else { + throw Error('Invalid number of arguments'); + } + + var d = getCoords(this, dx, dy); + + var w2 = sw / 2; + var h2 = sh / 2; + + var vmlStr = []; + + var W = 10; + var H = 10; + + // For some reason that I've now forgotten, using divs didn't work + vmlStr.push(' ' , + '', + ''); + + this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join('')); + }; + + contextPrototype.stroke = function(aFill) { + var lineStr = []; + var lineOpen = false; + + var W = 10; + var H = 10; + + lineStr.push(''); + + if (!aFill) { + appendStroke(this, lineStr); + } else { + appendFill(this, lineStr, min, max); + } + + lineStr.push(''); + + this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); + }; + + function appendStroke(ctx, lineStr) { + var a = processStyle(ctx.strokeStyle); + var color = a.color; + var opacity = a.alpha * ctx.globalAlpha; + var lineWidth = ctx.lineScale_ * ctx.lineWidth; + + // VML cannot correctly render a line if the width is less than 1px. + // In that case, we dilute the color to make the line look thinner. + if (lineWidth < 1) { + opacity *= lineWidth; + } + + lineStr.push( + '' + ); + } + + function appendFill(ctx, lineStr, min, max) { + var fillStyle = ctx.fillStyle; + var arcScaleX = ctx.arcScaleX_; + var arcScaleY = ctx.arcScaleY_; + var width = max.x - min.x; + var height = max.y - min.y; + if (fillStyle instanceof CanvasGradient_) { + // TODO: Gradients transformed with the transformation matrix. + var angle = 0; + var focus = {x: 0, y: 0}; + + // additional offset + var shift = 0; + // scale factor for offset + var expansion = 1; + + if (fillStyle.type_ == 'gradient') { + var x0 = fillStyle.x0_ / arcScaleX; + var y0 = fillStyle.y0_ / arcScaleY; + var x1 = fillStyle.x1_ / arcScaleX; + var y1 = fillStyle.y1_ / arcScaleY; + var p0 = getCoords(ctx, x0, y0); + var p1 = getCoords(ctx, x1, y1); + var dx = p1.x - p0.x; + var dy = p1.y - p0.y; + angle = Math.atan2(dx, dy) * 180 / Math.PI; + + // The angle should be a non-negative number. + if (angle < 0) { + angle += 360; + } + + // Very small angles produce an unexpected result because they are + // converted to a scientific notation string. + if (angle < 1e-6) { + angle = 0; + } + } else { + var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_); + focus = { + x: (p0.x - min.x) / width, + y: (p0.y - min.y) / height + }; + + width /= arcScaleX * Z; + height /= arcScaleY * Z; + var dimension = m.max(width, height); + shift = 2 * fillStyle.r0_ / dimension; + expansion = 2 * fillStyle.r1_ / dimension - shift; + } + + // We need to sort the color stops in ascending order by offset, + // otherwise IE won't interpret it correctly. + var stops = fillStyle.colors_; + stops.sort(function(cs1, cs2) { + return cs1.offset - cs2.offset; + }); + + var length = stops.length; + var color1 = stops[0].color; + var color2 = stops[length - 1].color; + var opacity1 = stops[0].alpha * ctx.globalAlpha; + var opacity2 = stops[length - 1].alpha * ctx.globalAlpha; + + var colors = []; + for (var i = 0; i < length; i++) { + var stop = stops[i]; + colors.push(stop.offset * expansion + shift + ' ' + stop.color); + } + + // When colors attribute is used, the meanings of opacity and o:opacity2 + // are reversed. + lineStr.push(''); + } else if (fillStyle instanceof CanvasPattern_) { + if (width && height) { + var deltaLeft = -min.x; + var deltaTop = -min.y; + lineStr.push(''); + } + } else { + var a = processStyle(ctx.fillStyle); + var color = a.color; + var opacity = a.alpha * ctx.globalAlpha; + lineStr.push(''); + } + } + + contextPrototype.fill = function() { + this.stroke(true); + }; + + contextPrototype.closePath = function() { + this.currentPath_.push({type: 'close'}); + }; + + function getCoords(ctx, aX, aY) { + var m = ctx.m_; + return { + x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, + y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 + }; + }; + + contextPrototype.save = function() { + var o = {}; + copyState(this, o); + this.aStack_.push(o); + this.mStack_.push(this.m_); + this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); + }; + + contextPrototype.restore = function() { + if (this.aStack_.length) { + copyState(this.aStack_.pop(), this); + this.m_ = this.mStack_.pop(); + } + }; + + function matrixIsFinite(m) { + return isFinite(m[0][0]) && isFinite(m[0][1]) && + isFinite(m[1][0]) && isFinite(m[1][1]) && + isFinite(m[2][0]) && isFinite(m[2][1]); + } + + function setM(ctx, m, updateLineScale) { + if (!matrixIsFinite(m)) { + return; + } + ctx.m_ = m; + + if (updateLineScale) { + // Get the line scale. + // Determinant of this.m_ means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; + ctx.lineScale_ = sqrt(abs(det)); + } + } + + contextPrototype.translate = function(aX, aY) { + var m1 = [ + [1, 0, 0], + [0, 1, 0], + [aX, aY, 1] + ]; + + setM(this, matrixMultiply(m1, this.m_), false); + }; + + contextPrototype.rotate = function(aRot) { + var c = mc(aRot); + var s = ms(aRot); + + var m1 = [ + [c, s, 0], + [-s, c, 0], + [0, 0, 1] + ]; + + setM(this, matrixMultiply(m1, this.m_), false); + }; + + contextPrototype.scale = function(aX, aY) { + this.arcScaleX_ *= aX; + this.arcScaleY_ *= aY; + var m1 = [ + [aX, 0, 0], + [0, aY, 0], + [0, 0, 1] + ]; + + setM(this, matrixMultiply(m1, this.m_), true); + }; + + contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { + var m1 = [ + [m11, m12, 0], + [m21, m22, 0], + [dx, dy, 1] + ]; + + setM(this, matrixMultiply(m1, this.m_), true); + }; + + contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { + var m = [ + [m11, m12, 0], + [m21, m22, 0], + [dx, dy, 1] + ]; + + setM(this, m, true); + }; + + /** + * The text drawing function. + * The maxWidth argument isn't taken in account, since no browser supports + * it yet. + */ + contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) { + var m = this.m_, + delta = 1000, + left = 0, + right = delta, + offset = {x: 0, y: 0}, + lineStr = []; + + var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_); + + var fontStyleString = buildStyle(fontStyle); + + var elementStyle = this.element_.currentStyle; + var textAlign = this.textAlign.toLowerCase(); + switch (textAlign) { + case 'left': + case 'center': + case 'right': + break; + case 'end': + textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left'; + break; + case 'start': + textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left'; + break; + default: + textAlign = 'left'; + } + + // 1.75 is an arbitrary number, as there is no info about the text baseline + switch (this.textBaseline) { + case 'hanging': + case 'top': + offset.y = fontStyle.size / 1.75; + break; + case 'middle': + break; + default: + case null: + case 'alphabetic': + case 'ideographic': + case 'bottom': + offset.y = -fontStyle.size / 2.25; + break; + } + + switch(textAlign) { + case 'right': + left = delta; + right = 0.05; + break; + case 'center': + left = right = delta / 2; + break; + } + + var d = getCoords(this, x + offset.x, y + offset.y); + + lineStr.push(''); + + if (stroke) { + appendStroke(this, lineStr); + } else { + // TODO: Fix the min and max params. + appendFill(this, lineStr, {x: -left, y: 0}, + {x: right, y: fontStyle.size}); + } + + var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' + + m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0'; + + var skewOffset = mr(d.x / Z + 1 - m[0][0]) + ',' + mr(d.y / Z - 2 * m[1][0]); + + + lineStr.push('', + '', + ''); + + this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); + }; + + contextPrototype.fillText = function(text, x, y, maxWidth) { + this.drawText_(text, x, y, maxWidth, false); + }; + + contextPrototype.strokeText = function(text, x, y, maxWidth) { + this.drawText_(text, x, y, maxWidth, true); + }; + + contextPrototype.measureText = function(text) { + if (!this.textMeasureEl_) { + var s = ''; + this.element_.insertAdjacentHTML('beforeEnd', s); + this.textMeasureEl_ = this.element_.lastChild; + } + var doc = this.element_.ownerDocument; + this.textMeasureEl_.innerHTML = ''; + this.textMeasureEl_.style.font = this.font; + // Don't use innerHTML or innerText because they allow markup/whitespace. + this.textMeasureEl_.appendChild(doc.createTextNode(text)); + return {width: this.textMeasureEl_.offsetWidth}; + }; + + /******** STUBS ********/ + contextPrototype.clip = function() { + // TODO: Implement + }; + + contextPrototype.arcTo = function() { + // TODO: Implement + }; + + contextPrototype.createPattern = function(image, repetition) { + return new CanvasPattern_(image, repetition); + }; + + // Gradient / Pattern Stubs + function CanvasGradient_(aType) { + this.type_ = aType; + this.x0_ = 0; + this.y0_ = 0; + this.r0_ = 0; + this.x1_ = 0; + this.y1_ = 0; + this.r1_ = 0; + this.colors_ = []; + } + + CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { + aColor = processStyle(aColor); + this.colors_.push({offset: aOffset, + color: aColor.color, + alpha: aColor.alpha}); + }; + + function CanvasPattern_(image, repetition) { + assertImageIsValid(image); + switch (repetition) { + case 'repeat': + case null: + case '': + this.repetition_ = 'repeat'; + break; + case 'repeat-x': + case 'repeat-y': + case 'no-repeat': + this.repetition_ = repetition; + break; + default: + throwException('SYNTAX_ERR'); + } + + this.src_ = image.src; + this.width_ = image.width; + this.height_ = image.height; + } + + function throwException(s) { + throw new DOMException_(s); + } + + function assertImageIsValid(img) { + if (!img || img.nodeType != 1 || img.tagName != 'IMG') { + throwException('TYPE_MISMATCH_ERR'); + } + if (img.readyState != 'complete') { + throwException('INVALID_STATE_ERR'); + } + } + + function DOMException_(s) { + this.code = this[s]; + this.message = s +': DOM Exception ' + this.code; + } + var p = DOMException_.prototype = new Error; + p.INDEX_SIZE_ERR = 1; + p.DOMSTRING_SIZE_ERR = 2; + p.HIERARCHY_REQUEST_ERR = 3; + p.WRONG_DOCUMENT_ERR = 4; + p.INVALID_CHARACTER_ERR = 5; + p.NO_DATA_ALLOWED_ERR = 6; + p.NO_MODIFICATION_ALLOWED_ERR = 7; + p.NOT_FOUND_ERR = 8; + p.NOT_SUPPORTED_ERR = 9; + p.INUSE_ATTRIBUTE_ERR = 10; + p.INVALID_STATE_ERR = 11; + p.SYNTAX_ERR = 12; + p.INVALID_MODIFICATION_ERR = 13; + p.NAMESPACE_ERR = 14; + p.INVALID_ACCESS_ERR = 15; + p.VALIDATION_ERR = 16; + p.TYPE_MISMATCH_ERR = 17; + + // set up externs + G_vmlCanvasManager = G_vmlCanvasManager_; + CanvasRenderingContext2D = CanvasRenderingContext2D_; + CanvasGradient = CanvasGradient_; + CanvasPattern = CanvasPattern_; + DOMException = DOMException_; + G_vmlCanvasManager._version = 888; +})(); + +} // if diff --git a/web/extensions/jqplot/excanvas.min.js b/web/extensions/jqplot/excanvas.min.js new file mode 100644 index 0000000..83373a5 --- /dev/null +++ b/web/extensions/jqplot/excanvas.min.js @@ -0,0 +1,3 @@ +/* jqPlot @VERSION | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;i.family="'"+i.family.replace(/(\'|\")/g,"").replace(/\s*,\s*/g,"', '")+"'";return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(aq,m){var aj,ah,al,ay,ao,am,at,aA;var ak=aq.runtimeStyle.width;var ap=aq.runtimeStyle.height;aq.runtimeStyle.width="auto";aq.runtimeStyle.height="auto";var ai=aq.width;var aw=aq.height;aq.runtimeStyle.width=ak;aq.runtimeStyle.height=ap;if(arguments.length==3){aj=arguments[1];ah=arguments[2];ao=am=0;at=al=ai;aA=ay=aw}else{if(arguments.length==5){aj=arguments[1];ah=arguments[2];al=arguments[3];ay=arguments[4];ao=am=0;at=ai;aA=aw}else{if(arguments.length==9){ao=arguments[1];am=arguments[2];at=arguments[3];aA=arguments[4];aj=arguments[5];ah=arguments[6];al=arguments[7];ay=arguments[8]}else{throw Error("Invalid number of arguments")}}}var az=V(this,aj,ah);var p=at/2;var j=aA/2;var ax=[];var i=10;var ag=10;ax.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",ax.join(""))};q.stroke=function(al){var aj=[];var Z=false;var m=10;var am=10;aj.push("ak.x){ak.x=j.x}if(ag.y==null||j.yak.y){ak.y=j.y}}}aj.push(' ">');if(!al){w(this,aj)}else{G(this,aj,ag,ak)}aj.push("");this.element_.insertAdjacentHTML("beforeEnd",aj.join(""))};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('')}}}q.fill=function(){this.stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/2.25;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d+1-ao[0][0])+","+n(aq.y/d-2*ao[1][0]);ag.push('','','');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error;X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P;G_vmlCanvasManager._version=888})()}; \ No newline at end of file diff --git a/web/extensions/jqplot/gpl-2.0.txt b/web/extensions/jqplot/gpl-2.0.txt new file mode 100644 index 0000000..864c6b8 --- /dev/null +++ b/web/extensions/jqplot/gpl-2.0.txt @@ -0,0 +1,280 @@ +Title: GPL Version 2 + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/web/extensions/jqplot/jqPlotCssStyling.txt b/web/extensions/jqplot/jqPlotCssStyling.txt new file mode 100644 index 0000000..041035d --- /dev/null +++ b/web/extensions/jqplot/jqPlotCssStyling.txt @@ -0,0 +1,53 @@ +Title: jqPlot CSS Customization + +Much of the styling of jqPlot is done by css. The jqPlot css file is, unremarkably, +jquery.jqplot.css and resides in the same directory as jqPlot itself. + +There exist some styling related javascript properties on the plot objects themselves +(like fontStyle, fontSize, etc.). These can be set with the options object at plot creation. +Generally, setting these options is *NOT* the preferred way to customize the look of the +plot. Use the css file instead. *These options are deprecated and may disappear*. The +exceptions are certain background and color options which control attributes of something +renderered on a canvas. This would be line color, grid background, etc. These must +be set by the options object. For a list of available options, see . + +Objects in the plot that can be customized by css are given a css class like ".jqplot-*". +For example, the plot title will have a ".jqplot-title" class, the axes ".jqplot-axis", etc. + +Currently assigned classes in jqPlot +are as follows: + +.jqplot-target - Styles for the plot target div. These will be cascaded down +to all plot elements according to css rules. + +.jqplot-axis - Styles for all axes + +.jqplot-xaxis - Styles applied to the primary x axis only. + +.jqplot-yaxis - Styles applied to the primary y axis only. + +.jqplot-x2axis, .jqplot-x3axis, ... - Styles applied to the 2nd, 3rd, etc. x axis only. + +.jqplot-y2axis, .jqplot-y3axis, ... - Styles applied to the 2nd, 3rd, etc.y axis only. + +.jqplot-axis-tick - Styles applied to all axis ticks + +.jqplot-xaxis-tick - Styles applied to primary x axis ticks only. + +.jqplot-x2axis-tick - Styles applied to secondary x axis ticks only. + +.jqplot-yaxis-tick - Styles applied to primary y axis ticks only. + +.jqplot-y2axis-tick - Styles applied to secondary y axis ticks only. + +table.jqplot-table-legend - Styles applied to the legend box table. + +.jqplot-title - Styles applied to the title. + +.jqplot-cursor-tooltip - Styles applied to the cursor tooltip + +.jqplot-highlighter-tooltip - Styles applied to the highlighter tooltip. + +div.jqplot-table-legend-swatch - the div element used for the colored swatch on the legend. + +Note that axes will be assigned 2 classes like: class=".jqplot-axis .jqplot-xaxis". \ No newline at end of file diff --git a/web/extensions/jqplot/jqPlotOptions.txt b/web/extensions/jqplot/jqPlotOptions.txt new file mode 100644 index 0000000..3f58a13 --- /dev/null +++ b/web/extensions/jqplot/jqPlotOptions.txt @@ -0,0 +1,391 @@ +Title: jqPlot Options + +**This document is out of date. While the options described here should still be +relavent and valid, it has not been updated for many new options. Sorry for +this inconvenience.** + +This document describes the options available to jqPlot. These are set with the +third argument to the $.jqplot('target', data, options) function. Options are +described using the following convention: + +{{{ +property: default, // notes +}}} + +This document is not complete! Not all options are shown! +Further information about the options can be found in the online API +documentation. For details on how the options relate to the API documentation, +see the in the optionsTutorial.txt file. + +{{{ +options = +{ + seriesColors: [ "#4bb2c5", "#c5b47f", "#EAA228", "#579575", "#839557", "#958c12", + "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc"], // colors that will + // be assigned to the series. If there are more series than colors, colors + // will wrap around and start at the beginning again. + + // when fillToZero is enabled, this sets the colors to use for portions of the line below zero. + negativeSeriesColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", + "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", + "#959E5C", "#C7AF7B", "#478396", "#907294"], + + sortData : true, // if true, will sort the data passed in by the user. + stackSeries: false, // if true, will create a stack plot. + // Currently supported by line and bar graphs. + + title: '', // Title for the plot. Can also be specified as an object like: + + title: { + text: '', // title for the plot, + show: true, + }, + + animate : false, // if true, the series will be animated on initial drawing. + // This support is renderer-dependent; the renderer must support animation. + animateReplot : false, // if true, the series will be animated after every replot() call. + // Use with caution! Replots can happen very frequently under + // certain circumstances (e.g. resizing, dragging points) and + // animation in these situations can cause problems. + captureRightClick : false, // if true, right-click events are intercepted and a jqplotRightClick + // event will be fired. This will also block the context menu. + dataRenderer : undefined, // A callable which can be used to preprocess data passed into the plot. + // Will be called with 3 arguments: the plot data, a reference to the plot, + // and the value of dataRendererOptions. + + dataRendererOptions : undefined, // Options that will be passed to the dataRenderer, + // if that option is supplied. Can be of any type. + + gridData : [], // array of grid coordinates corresponding to the data points; + // normally jqPlot will calculate this for you. + + axesDefaults: { + show: false, // whether or not to render the axis. Determined automatically. + min: null, // minimum numerical value of the axis. Determined automatically. + max: null, // maximum numerical value of the axis. Determined automatically. + pad: 1.2, // a factor multiplied by the data range on the axis to give the + // axis range so that data points don't fall on the edges of the axis. + ticks: [], // a 1D [val1, val2, ...], or 2D [[val, label], [val, label], ...] + // array of ticks to use. Computed automatically. + numberTicks: undefined, + renderer: $.jqplot.LinearAxisRenderer, // renderer to use to draw the axis, + rendererOptions: {}, // options to pass to the renderer. LinearAxisRenderer + // has no options, + tickOptions: { + mark: 'outside', // Where to put the tick mark on the axis + // 'outside', 'inside' or 'cross' + showMark: true, // whether or not to show the mark on the axis + showGridline: true, // whether to draw a gridline (across the whole grid) at this tick + isMinorTick: false, // whether this is a minor tick + markSize: 4, // length the tick will extend beyond the grid in pixels. For + // 'cross', length will be added above and below the grid boundary + show: true, // whether to show the tick (mark and label) + showLabel: true, // whether to show the text label at the tick + prefix: '', // String to prepend to the tick label. + // Prefix is prepended to the formatted tick label + suffix: '', // String to append to the tick label. + // Suffix is appended to the formatted tick label + formatString: '', // format string to use with the axis tick formatter + fontFamily: '', // css spec for the font-size css attribute + fontSize: '', // css spec for the font-size css attribute + textColor: '', // css spec for the color attribute + escapeHTML: false // true to escape HTML entities in the label + } + showTicks: true, // whether or not to show the tick labels, + showTickMarks: true, // whether or not to show the tick marks + }, + + axes: { + xaxis: { + // same options as axesDefaults + }, + yaxis: { + // same options as axesDefaults + }, + x2axis: { + // same options as axesDefaults + }, + y2axis: { + // same options as axesDefaults + } + }, + + seriesDefaults: { + show: true, // whether to render the series. + xaxis: 'xaxis', // either 'xaxis' or 'x2axis'. + yaxis: 'yaxis', // either 'yaxis' or 'y2axis'. + label: '', // label to use in the legend for this line. + color: '', // CSS color spec to use for the line. Determined automatically. + lineWidth: 2.5, // Width of the line in pixels. + shadow: true, // show shadow or not. + shadowAngle: 45, // angle (degrees) of the shadow, clockwise from x axis. + shadowOffset: 1.25, // offset from the line of the shadow. + shadowDepth: 3, // Number of strokes to make when drawing shadow. Each + // stroke offset by shadowOffset from the last. + shadowAlpha: 0.1, // Opacity of the shadow. + showLine: true, // whether to render the line segments or not. + showMarker: true, // render the data point markers or not. + fill: false, // fill under the line, + fillAndStroke: false, // stroke a line at top of fill area. + fillColor: undefined, // custom fill color for filled lines (default is line color). + fillAlpha: undefined, // custom alpha to apply to fillColor. + renderer: $.jqplot.LineRenderer], // renderer used to draw the series. + rendererOptions: {}, // options passed to the renderer. LineRenderer has no options. + markerRenderer: $.jqplot.MarkerRenderer, // renderer to use to draw the data + // point markers. + markerOptions: { + show: true, // whether to show data point markers. + style: 'filledCircle', // circle, diamond, square, filledCircle. + // filledDiamond or filledSquare. + lineWidth: 2, // width of the stroke drawing the marker. + size: 9, // size (diameter, edge length, etc.) of the marker. + color: '#666666' // color of marker, set to color of line by default. + shadow: true, // whether to draw shadow on marker or not. + shadowAngle: 45, // angle of the shadow. Clockwise from x axis. + shadowOffset: 1, // offset from the line of the shadow, + shadowDepth: 3, // Number of strokes to make when drawing shadow. Each stroke + // offset by shadowOffset from the last. + shadowAlpha: 0.07 // Opacity of the shadow + } + }, + + series:[ + {Each series has same options as seriesDefaults}, + {You can override each series individually here} + ], + + legend: { + show: false, + location: 'ne', // compass direction, nw, n, ne, e, se, s, sw, w. + xoffset: 12, // pixel offset of the legend box from the x (or x2) axis. + yoffset: 12, // pixel offset of the legend box from the y (or y2) axis. + }, + + grid: { + drawGridLines: true, // whether to draw lines across the grid or not. + gridLineColor: '#cccccc' // Color of the grid lines. + background: '#fffdf6', // CSS color spec for background color of grid. + borderColor: '#999999', // CSS color spec for border around grid. + borderWidth: 2.0, // pixel width of border around grid. + shadow: true, // draw a shadow for grid. + shadowAngle: 45, // angle of the shadow. Clockwise from x axis. + shadowOffset: 1.5, // offset from the line of the shadow. + shadowWidth: 3, // width of the stroke for the shadow. + shadowDepth: 3, // Number of strokes to make when drawing shadow. + // Each stroke offset by shadowOffset from the last. + shadowAlpha: 0.07 // Opacity of the shadow + renderer: $.jqplot.CanvasGridRenderer, // renderer to use to draw the grid. + rendererOptions: {} // options to pass to the renderer. Note, the default + // CanvasGridRenderer takes no additional options. + }, + + // Size of the grid containing the plot. + gridDimensions: { + height: null, + width: null + }, + + // Padding to apply around the grid containing the plot. + gridPadding: { + top: null, + bottom: null, + left: null, + right: null + }, + + noDataIndicator : object, // For setting up a mock plot with a data loading indicator if + // no data is specified. Must have .show=true, .axes, and a + // .indicator string that will be displayed. + + // Plugin and renderer options. + + // BarRenderer. + // With BarRenderer, you can specify additional options in the rendererOptions object + // on the series or on the seriesDefaults object. Note, some options are re-specified + // (like shadowDepth) to override lineRenderer defaults from which BarRenderer inherits. + + seriesDefaults: { + rendererOptions: { + barPadding: 8, // number of pixels between adjacent bars in the same + // group (same category or bin). + barMargin: 10, // number of pixels between adjacent groups of bars. + barDirection: 'vertical', // vertical or horizontal. + barWidth: null, // width of the bars. null to calculate automatically. + shadowOffset: 2, // offset from the bar edge to stroke the shadow. + shadowDepth: 5, // number of strokes to make for the shadow. + shadowAlpha: 0.8, // transparency of the shadow. + } + }, + + // Cursor + // Options are passed to the cursor plugin through the "cursor" object at the top + // level of the options object. + + cursor: { + style: 'crosshair', // A CSS spec for the cursor type to change the + // cursor to when over plot. + show: true, + showTooltip: true, // show a tooltip showing cursor position. + followMouse: false, // whether tooltip should follow the mouse or be stationary. + tooltipLocation: 'se', // location of the tooltip either relative to the mouse + // (followMouse=true) or relative to the plot. One of + // the compass directions, n, ne, e, se, etc. + tooltipOffset: 6, // pixel offset of the tooltip from the mouse or the axes. + showTooltipGridPosition: false, // show the grid pixel coordinates of the mouse + // in the tooltip. + showTooltipUnitPosition: true, // show the coordinates in data units of the mouse + // in the tooltip. + tooltipFormatString: '%.4P', // sprintf style format string for tooltip values. + useAxesFormatters: true, // whether to use the same formatter and formatStrings + // as used by the axes, or to use the formatString + // specified on the cursor with sprintf. + tooltipAxesGroups: [], // show only specified axes groups in tooltip. Would specify like: + // [['xaxis', 'yaxis'], ['xaxis', 'y2axis']]. By default, all axes + // combinations with for the series in the plot are shown. + + }, + + // Dragable + // Dragable options are specified with the "dragable" object at the top level + // of the options object. + // (Note that 'dragable' is the name and spelling used by the plugin, even though + // the correct word is 'draggable'.) + + dragable: { + color: undefined, // custom color to use for the dragged point and dragged line + // section. default will use a transparent variant of the line color. + constrainTo: 'none', // Constrain dragging motion to an axis: 'x', 'y', or 'none'. + }, + + // Highlighter + // Highlighter options are specified with the "highlighter" object at the top level + // of the options object. + + highlighter: { + lineWidthAdjust: 2.5, // pixels to add to the size line stroking the data point marker + // when showing highlight. Only affects non filled data point markers. + sizeAdjust: 5, // pixels to add to the size of filled markers when drawing highlight. + showTooltip: true, // show a tooltip with data point values. + tooltipLocation: 'nw', // location of tooltip: n, ne, e, se, s, sw, w, nw. + fadeTooltip: true, // use fade effect to show/hide tooltip. + tooltipFadeSpeed: "fast"// slow, def, fast, or a number of milliseconds. + tooltipOffset: 2, // pixel offset of tooltip from the highlight. + tooltipAxes: 'both', // which axis values to display in the tooltip, x, y or both. + tooltipSeparator: ', ' // separator between values in the tooltip. + useAxesFormatters: true // use the same format string and formatters as used in the axes to + // display values in the tooltip. + tooltipFormatString: '%.5P' // sprintf format string for the tooltip. only used if + // useAxesFormatters is false. Will use sprintf formatter with + // this string, not the axes formatters. + }, + + // LogAxisRenderer + // LogAxisRenderer add 2 options to the axes object. These options are specified directly on + // the axes or axesDefaults object. + + axesDefaults: { + base: 10, // the logarithmic base. + tickDistribution: 'even', // 'even' or 'power'. 'even' will produce ticks with even visual + // (pixel) spacing on the axis. 'power' will produce ticks spaced by + // increasing powers of the log base. + }, + + // PieRenderer + // PieRenderer accepts options from the rendererOptions object of the series or seriesDefaults object. + + seriesDefaults: { + rendererOptions: { + diameter: undefined, // diameter of pie, auto computed by default. + padding: 20, // padding between pie and neighboring legend or plot margin. + sliceMargin: 0, // gap between slices. + fill: true, // render solid (filled) slices. + shadowOffset: 2, // offset of the shadow from the chart. + shadowDepth: 5, // Number of strokes to make when drawing shadow. Each stroke is + // offset by shadowOffset from the last. + shadowAlpha: 0.07 // Opacity of the shadow + } + }, + + // Trendline + // Trendline takes options on the trendline object of the series or seriesDefaults object. + + seriesDefaults: { + trendline: { + show: true, // show the trend line + color: '#666666', // CSS color spec for the trend line. + label: '', // label for the trend line. + type: 'linear', // 'linear', 'exponential' or 'exp' + shadow: true, // show the trend line shadow. + lineWidth: 1.5, // width of the trend line. + shadowAngle: 45, // angle of the shadow. Clockwise from x axis. + shadowOffset: 1.5, // offset from the line of the shadow. + shadowDepth: 3, // Number of strokes to make when drawing shadow. + // Each stroke offset by shadowOffset from the last. + shadowAlpha: 0.07 // Opacity of the shadow + } + } +} +}}} + + +Options to be described: + + lineRenderer: + .markerOptions? + bands + fill + fillAndStroke + fillStyle + highlightColor + highlightMouseDown + highlightMouseOver + shadow + shadowOffset + showLine + + shadowRenderer: + alpha + closePath + depth + fill + fillRect + fillStyle + isarc + lineCap + lineJoin + linePattern + lineWidth + offset + strokeStyle + + shapeRenderer: + clearRect + closePath + fill + fillRect + fillStyle + isarc + lineCap + lineJoin + linePattern + lineWidth + strokeRect + strokeStyle + + jqplot.effects: + options.duration ; options.complete + + LinearAxisRenderer: + .min, .max (?) + numberTicks + tickInternal + forceTickAt0 : false, // If true, a tick will always be drawn at 0. + + markerRenderer: + color + fillStyle + strokeStyle + + canvasGridRenderer: + lineWidth + diff --git a/web/extensions/jqplot/jquery.jqplot.css b/web/extensions/jqplot/jquery.jqplot.css new file mode 100644 index 0000000..f6768a6 --- /dev/null +++ b/web/extensions/jqplot/jquery.jqplot.css @@ -0,0 +1,259 @@ +/*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ +.jqplot-target { + position: relative; + color: #666666; + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 1em; +/* height: 300px; + width: 400px;*/ +} + +/*rules applied to all axes*/ +.jqplot-axis { + font-size: 0.75em; +} + +.jqplot-xaxis { + margin-top: 10px; +} + +.jqplot-x2axis { + margin-bottom: 10px; +} + +.jqplot-yaxis { + margin-right: 10px; +} + +.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis { + margin-left: 10px; + margin-right: 10px; +} + +/*rules applied to all axis tick divs*/ +.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick { + position: absolute; + white-space: pre; +} + + +.jqplot-xaxis-tick { + top: 0px; + /* initial position untill tick is drawn in proper place */ + left: 15px; +/* padding-top: 10px;*/ + vertical-align: top; +} + +.jqplot-x2axis-tick { + bottom: 0px; + /* initial position untill tick is drawn in proper place */ + left: 15px; +/* padding-bottom: 10px;*/ + vertical-align: bottom; +} + +.jqplot-yaxis-tick { + right: 0px; + /* initial position untill tick is drawn in proper place */ + top: 15px; +/* padding-right: 10px;*/ + text-align: right; +} + +.jqplot-yaxis-tick.jqplot-breakTick { + right: -20px; + margin-right: 0px; + padding:1px 5px 1px 5px; + /*background-color: white;*/ + z-index: 2; + font-size: 1.5em; +} + +.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { + left: 0px; + /* initial position untill tick is drawn in proper place */ + top: 15px; +/* padding-left: 10px;*/ +/* padding-right: 15px;*/ + text-align: left; +} + +.jqplot-yMidAxis-tick { + text-align: center; + white-space: nowrap; +} + +.jqplot-xaxis-label { + margin-top: 10px; + font-size: 11pt; + position: absolute; +} + +.jqplot-x2axis-label { + margin-bottom: 10px; + font-size: 11pt; + position: absolute; +} + +.jqplot-yaxis-label { + margin-right: 10px; +/* text-align: center;*/ + font-size: 11pt; + position: absolute; +} + +.jqplot-yMidAxis-label { + font-size: 11pt; + position: absolute; +} + +.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label { +/* text-align: center;*/ + font-size: 11pt; + margin-left: 10px; + position: absolute; +} + +.jqplot-meterGauge-tick { + font-size: 0.75em; + color: #999999; +} + +.jqplot-meterGauge-label { + font-size: 1em; + color: #999999; +} + +table.jqplot-table-legend { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; +} + +table.jqplot-table-legend, table.jqplot-cursor-legend { + background-color: rgba(255,255,255,0.6); + border: 1px solid #cccccc; + position: absolute; + font-size: 0.75em; +} + +td.jqplot-table-legend { + vertical-align:middle; +} + +/* +These rules could be used instead of assigning +element styles and relying on js object properties. +*/ + +/* +td.jqplot-table-legend-swatch { + padding-top: 0.5em; + text-align: center; +} + +tr.jqplot-table-legend:first td.jqplot-table-legend-swatch { + padding-top: 0px; +} +*/ + +td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { + cursor: pointer; +} + +.jqplot-table-legend .jqplot-series-hidden { + text-decoration: line-through; +} + +div.jqplot-table-legend-swatch-outline { + border: 1px solid #cccccc; + padding:1px; +} + +div.jqplot-table-legend-swatch { + width:0px; + height:0px; + border-top-width: 5px; + border-bottom-width: 5px; + border-left-width: 6px; + border-right-width: 6px; + border-top-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-right-style: solid; +} + +.jqplot-title { + top: 0px; + left: 0px; + padding-bottom: 0.5em; + font-size: 1.2em; +} + +table.jqplot-cursor-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; +} + + +.jqplot-cursor-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; + white-space: nowrap; + background: rgba(208,208,208,0.5); + padding: 1px; +} + +.jqplot-highlighter-tooltip, .jqplot-canvasOverlay-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; + white-space: nowrap; + background: rgba(208,208,208,0.5); + padding: 1px; +} + +.jqplot-point-label { + font-size: 0.75em; + z-index: 2; +} + +td.jqplot-cursor-legend-swatch { + vertical-align: middle; + text-align: center; +} + +div.jqplot-cursor-legend-swatch { + width: 1.2em; + height: 0.7em; +} + +.jqplot-error { +/* Styles added to the plot target container when there is an error go here.*/ + text-align: center; +} + +.jqplot-error-message { +/* Styling of the custom error message div goes here.*/ + position: relative; + top: 46%; + display: inline-block; +} + +div.jqplot-bubble-label { + font-size: 0.8em; +/* background: rgba(90%, 90%, 90%, 0.15);*/ + padding-left: 2px; + padding-right: 2px; + color: rgb(20%, 20%, 20%); +} + +div.jqplot-bubble-label.jqplot-bubble-label-highlight { + background: rgba(90%, 90%, 90%, 0.7); +} + +div.jqplot-noData-container { + text-align: center; + background-color: rgba(96%, 96%, 96%, 0.3); +} diff --git a/web/extensions/jqplot/jquery.jqplot.js b/web/extensions/jqplot/jquery.jqplot.js new file mode 100644 index 0000000..5004c98 --- /dev/null +++ b/web/extensions/jqplot/jquery.jqplot.js @@ -0,0 +1,11411 @@ +/** + * Title: jqPlot Charts + * + * Pure JavaScript plotting plugin for jQuery. + * + * About: Version + * + * version: 1.0.8 + * revision: 1250 + * + * About: Copyright & License + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT and GPL version 2.0 licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * See and contained within this distribution for further information. + * + * The author would appreciate an email letting him know of any substantial + * use of jqPlot. You can reach the author at: chris at jqplot dot com + * or see http://www.jqplot.com/info.php. This is, of course, not required. + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php. + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + * + * About: Introduction + * + * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution. + * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally + * the excanvas script for IE support in your web page: + * + * > + * > + * > + * > + * + * jqPlot can be customized by overriding the defaults of any of the objects which make + * up the plot. The general usage of jqplot is: + * + * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject}); + * + * The options available to jqplot are detailed in in the jqPlotOptions.txt file. + * + * An actual call to $.jqplot() may look like the + * examples below: + * + * > chart = $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]); + * + * or + * + * > dataArray = [34,12,43,55,77]; + * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}}); + * + * For more inforrmation, see . + * + * About: Usage + * + * See + * + * About: Available Options + * + * See for a list of options available thorugh the options object (not complete yet!) + * + * About: Options Usage + * + * See + * + * About: Changes + * + * See + * + */ + +(function($) { + // make sure undefined is undefined + var undefined; + + $.fn.emptyForce = function() { + for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + $.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + if ($.jqplot.use_excanvas) { + elem.outerHTML = ""; + } + else { + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + elem = null; + } + + return $(this); + }; + + $.fn.removeChildForce = function(parent) { + while ( parent.firstChild ) { + this.removeChildForce( parent.firstChild ); + parent.removeChild( parent.firstChild ); + } + }; + + $.fn.jqplot = function() { + var datas = []; + var options = []; + // see how many data arrays we have + for (var i=0, l=arguments.length; i'+msg+''); + $('#'+target).addClass('jqplot-error'); + document.getElementById(target).style.background = $.jqplot.config.errorBackground; + document.getElementById(target).style.border = $.jqplot.config.errorBorder; + document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily; + document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize; + document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle; + document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight; + } + } + else { + plot.init(target, _data, _options); + plot.draw(); + plot.themeEngine.init.call(plot); + return plot; + } + }; + + $.jqplot.version = "1.0.8"; + $.jqplot.revision = "1250"; + + $.jqplot.targetCounter = 1; + + // canvas manager to reuse canvases on the plot. + // Should help solve problem of canvases not being freed and + // problem of waiting forever for firefox to decide to free memory. + $.jqplot.CanvasManager = function() { + // canvases are managed globally so that they can be reused + // across plots after they have been freed + if (typeof $.jqplot.CanvasManager.canvases == 'undefined') { + $.jqplot.CanvasManager.canvases = []; + $.jqplot.CanvasManager.free = []; + } + + var myCanvases = []; + + this.getCanvas = function() { + var canvas; + var makeNew = true; + + if (!$.jqplot.use_excanvas) { + for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) { + if ($.jqplot.CanvasManager.free[i] === true) { + makeNew = false; + canvas = $.jqplot.CanvasManager.canvases[i]; + // $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse'); + $.jqplot.CanvasManager.free[i] = false; + myCanvases.push(i); + break; + } + } + } + + if (makeNew) { + canvas = document.createElement('canvas'); + myCanvases.push($.jqplot.CanvasManager.canvases.length); + $.jqplot.CanvasManager.canvases.push(canvas); + $.jqplot.CanvasManager.free.push(false); + } + + return canvas; + }; + + // this method has to be used after settings the dimesions + // on the element returned by getCanvas() + this.initCanvas = function(canvas) { + if ($.jqplot.use_excanvas) { + return window.G_vmlCanvasManager.initElement(canvas); + } + return canvas; + }; + + this.freeAllCanvases = function() { + for (var i = 0, l=myCanvases.length; i < l; i++) { + this.freeCanvas(myCanvases[i]); + } + myCanvases = []; + }; + + this.freeCanvas = function(idx) { + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + // excanvas can't be reused, but properly unset + window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]); + $.jqplot.CanvasManager.canvases[idx] = null; + } + else { + var canvas = $.jqplot.CanvasManager.canvases[idx]; + canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); + $(canvas).unbind().removeAttr('class').removeAttr('style'); + // Style attributes seemed to be still hanging around. wierd. Some ticks + // still retained a left: 0px attribute after reusing a canvas. + $(canvas).css({left: '', top: '', position: ''}); + // setting size to 0 may save memory of unused canvases? + canvas.width = 0; + canvas.height = 0; + $.jqplot.CanvasManager.free[idx] = true; + } + }; + + }; + + + // Convienence function that won't hang IE or FF without FireBug. + $.jqplot.log = function() { + if (window.console) { + window.console.log.apply(window.console, arguments); + } + }; + + $.jqplot.config = { + addDomReference: false, + enablePlugins:false, + defaultHeight:300, + defaultWidth:400, + UTCAdjust:false, + timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000), + errorMessage: '', + errorBackground: '', + errorBorder: '', + errorFontFamily: '', + errorFontSize: '', + errorFontStyle: '', + errorFontWeight: '', + catchErrors: false, + defaultTickFormatString: "%.1f", + defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], + defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"], + dashLength: 4, + gapLength: 4, + dotGapLength: 2.5, + srcLocation: 'jqplot/src/', + pluginLocation: 'jqplot/src/plugins/' + }; + + + $.jqplot.arrayMax = function( array ){ + return Math.max.apply( Math, array ); + }; + + $.jqplot.arrayMin = function( array ){ + return Math.min.apply( Math, array ); + }; + + $.jqplot.enablePlugins = $.jqplot.config.enablePlugins; + + // canvas related tests taken from modernizer: + // Copyright (c) 2009 - 2010 Faruk Ates. + // http://www.modernizr.com + + $.jqplot.support_canvas = function() { + if (typeof $.jqplot.support_canvas.result == 'undefined') { + $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext; + } + return $.jqplot.support_canvas.result; + }; + + $.jqplot.support_canvas_text = function() { + if (typeof $.jqplot.support_canvas_text.result == 'undefined') { + if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) { + $.jqplot.support_canvas_text.result = true; + } + else { + $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); + } + + } + return $.jqplot.support_canvas_text.result; + }; + + $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false; + + /** + * + * Hooks: jqPlot Pugin Hooks + * + * $.jqplot.preInitHooks - called before initialization. + * $.jqplot.postInitHooks - called after initialization. + * $.jqplot.preParseOptionsHooks - called before user options are parsed. + * $.jqplot.postParseOptionsHooks - called after user options are parsed. + * $.jqplot.preDrawHooks - called before plot draw. + * $.jqplot.postDrawHooks - called after plot draw. + * $.jqplot.preDrawSeriesHooks - called before each series is drawn. + * $.jqplot.postDrawSeriesHooks - called after each series is drawn. + * $.jqplot.preDrawLegendHooks - called before the legend is drawn. + * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins + * can add rows to the legend table. + * $.jqplot.preSeriesInitHooks - called before series is initialized. + * $.jqplot.postSeriesInitHooks - called after series is initialized. + * $.jqplot.preParseSeriesOptionsHooks - called before series related options + * are parsed. + * $.jqplot.postParseSeriesOptionsHooks - called after series related options + * are parsed. + * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds + * listeners to the event canvas which lays on top of the grid area. + * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn. + * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn. + * + */ + + $.jqplot.preInitHooks = []; + $.jqplot.postInitHooks = []; + $.jqplot.preParseOptionsHooks = []; + $.jqplot.postParseOptionsHooks = []; + $.jqplot.preDrawHooks = []; + $.jqplot.postDrawHooks = []; + $.jqplot.preDrawSeriesHooks = []; + $.jqplot.postDrawSeriesHooks = []; + $.jqplot.preDrawLegendHooks = []; + $.jqplot.addLegendRowHooks = []; + $.jqplot.preSeriesInitHooks = []; + $.jqplot.postSeriesInitHooks = []; + $.jqplot.preParseSeriesOptionsHooks = []; + $.jqplot.postParseSeriesOptionsHooks = []; + $.jqplot.eventListenerHooks = []; + $.jqplot.preDrawSeriesShadowHooks = []; + $.jqplot.postDrawSeriesShadowHooks = []; + + // A superclass holding some common properties and methods. + $.jqplot.ElemContainer = function() { + this._elem; + this._plotWidth; + this._plotHeight; + this._plotDimensions = {height:null, width:null}; + }; + + $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) { + this._offsets = offsets; + var klass = clss || 'jqplot'; + var elem = document.createElement(el); + this._elem = $(elem); + this._elem.addClass(klass); + this._elem.css(cssopts); + this._elem.attr(attrib); + // avoid memory leak; + elem = null; + return this._elem; + }; + + $.jqplot.ElemContainer.prototype.getWidth = function() { + if (this._elem) { + return this._elem.outerWidth(true); + } + else { + return null; + } + }; + + $.jqplot.ElemContainer.prototype.getHeight = function() { + if (this._elem) { + return this._elem.outerHeight(true); + } + else { + return null; + } + }; + + $.jqplot.ElemContainer.prototype.getPosition = function() { + if (this._elem) { + return this._elem.position(); + } + else { + return {top:null, left:null, bottom:null, right:null}; + } + }; + + $.jqplot.ElemContainer.prototype.getTop = function() { + return this.getPosition().top; + }; + + $.jqplot.ElemContainer.prototype.getLeft = function() { + return this.getPosition().left; + }; + + $.jqplot.ElemContainer.prototype.getBottom = function() { + return this._elem.css('bottom'); + }; + + $.jqplot.ElemContainer.prototype.getRight = function() { + return this._elem.css('right'); + }; + + + /** + * Class: Axis + * An individual axis object. Cannot be instantiated directly, but created + * by the Plot object. Axis properties can be set or overridden by the + * options passed in from the user. + * + */ + function Axis(name) { + $.jqplot.ElemContainer.call(this); + // Group: Properties + // + // Axes options are specified within an axes object at the top level of the + // plot options like so: + // > { + // > axes: { + // > xaxis: {min: 5}, + // > yaxis: {min: 2, max: 8, numberTicks:4}, + // > x2axis: {pad: 1.5}, + // > y2axis: {ticks:[22, 44, 66, 88]} + // > } + // > } + // There are 2 x axes, 'xaxis' and 'x2axis', and + // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified. + this.name = name; + this._series = []; + // prop: show + // Wether to display the axis on the graph. + this.show = false; + // prop: tickRenderer + // A class of a rendering engine for creating the ticks labels displayed on the plot, + // See <$.jqplot.AxisTickRenderer>. + this.tickRenderer = $.jqplot.AxisTickRenderer; + // prop: tickOptions + // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options. + this.tickOptions = {}; + // prop: labelRenderer + // A class of a rendering engine for creating an axis label. + this.labelRenderer = $.jqplot.AxisLabelRenderer; + // prop: labelOptions + // Options passed to the label renderer. + this.labelOptions = {}; + // prop: label + // Label for the axis + this.label = null; + // prop: showLabel + // true to show the axis label. + this.showLabel = true; + // prop: min + // minimum value of the axis (in data units, not pixels). + this.min = null; + // prop: max + // maximum value of the axis (in data units, not pixels). + this.max = null; + // prop: autoscale + // DEPRECATED + // the default scaling algorithm produces superior results. + this.autoscale = false; + // prop: pad + // Padding to extend the range above and below the data bounds. + // The data range is multiplied by this factor to determine minimum and maximum axis bounds. + // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0. + this.pad = 1.2; + // prop: padMax + // Padding to extend the range above data bounds. + // The top of the data range is multiplied by this factor to determine maximum axis bounds. + // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0. + this.padMax = null; + // prop: padMin + // Padding to extend the range below data bounds. + // The bottom of the data range is multiplied by this factor to determine minimum axis bounds. + // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0. + this.padMin = null; + // prop: ticks + // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis. + // If no label is specified, the value is formatted into an appropriate label. + this.ticks = []; + // prop: numberTicks + // Desired number of ticks. Default is to compute automatically. + this.numberTicks; + // prop: tickInterval + // number of units between ticks. Mutually exclusive with numberTicks. + this.tickInterval; + // prop: renderer + // A class of a rendering engine that handles tick generation, + // scaling input data to pixel grid units and drawing the axis element. + this.renderer = $.jqplot.LinearAxisRenderer; + // prop: rendererOptions + // renderer specific options. See <$.jqplot.LinearAxisRenderer> for options. + this.rendererOptions = {}; + // prop: showTicks + // Wether to show the ticks (both marks and labels) or not. + // Will not override showMark and showLabel options if specified on the ticks themselves. + this.showTicks = true; + // prop: showTickMarks + // Wether to show the tick marks (line crossing grid) or not. + // Overridden by showTicks and showMark option of tick itself. + this.showTickMarks = true; + // prop: showMinorTicks + // Wether or not to show minor ticks. This is renderer dependent. + this.showMinorTicks = true; + // prop: drawMajorGridlines + // True to draw gridlines for major axis ticks. + this.drawMajorGridlines = true; + // prop: drawMinorGridlines + // True to draw gridlines for minor ticks. + this.drawMinorGridlines = false; + // prop: drawMajorTickMarks + // True to draw tick marks for major axis ticks. + this.drawMajorTickMarks = true; + // prop: drawMinorTickMarks + // True to draw tick marks for minor ticks. This is renderer dependent. + this.drawMinorTickMarks = true; + // prop: useSeriesColor + // Use the color of the first series associated with this axis for the + // tick marks and line bordering this axis. + this.useSeriesColor = false; + // prop: borderWidth + // width of line stroked at the border of the axis. Defaults + // to the width of the grid boarder. + this.borderWidth = null; + // prop: borderColor + // color of the border adjacent to the axis. Defaults to grid border color. + this.borderColor = null; + // prop: scaleToHiddenSeries + // True to include hidden series when computing axes bounds and scaling. + this.scaleToHiddenSeries = false; + // minimum and maximum values on the axis. + this._dataBounds = {min:null, max:null}; + // statistics (min, max, mean) as well as actual data intervals for each series attached to axis. + // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis. + this._intervalStats = []; + // pixel position from the top left of the min value and max value on the axis. + this._offsets = {min:null, max:null}; + this._ticks=[]; + this._label = null; + // prop: syncTicks + // true to try and synchronize tick spacing across multiple axes so that ticks and + // grid lines line up. This has an impact on autoscaling algorithm, however. + // In general, autoscaling an individual axis will work better if it does not + // have to sync ticks. + this.syncTicks = null; + // prop: tickSpacing + // Approximate pixel spacing between ticks on graph. Used during autoscaling. + // This number will be an upper bound, actual spacing will be less. + this.tickSpacing = 75; + // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks + // so they can be restored if altered by plugins. + this._min = null; + this._max = null; + this._tickInterval = null; + this._numberTicks = null; + this.__ticks = null; + // hold original user options. + this._options = {}; + } + + Axis.prototype = new $.jqplot.ElemContainer(); + Axis.prototype.constructor = Axis; + + Axis.prototype.init = function() { + if ($.isFunction(this.renderer)) { + this.renderer = new this.renderer(); + } + // set the axis name + this.tickOptions.axis = this.name; + // if showMark or showLabel tick options not specified, use value of axis option. + // showTicks overrides showTickMarks. + if (this.tickOptions.showMark == null) { + this.tickOptions.showMark = this.showTicks; + } + if (this.tickOptions.showMark == null) { + this.tickOptions.showMark = this.showTickMarks; + } + if (this.tickOptions.showLabel == null) { + this.tickOptions.showLabel = this.showTicks; + } + + if (this.label == null || this.label == '') { + this.showLabel = false; + } + else { + this.labelOptions.label = this.label; + } + if (this.showLabel == false) { + this.labelOptions.show = false; + } + // set the default padMax, padMin if not specified + // special check, if no padding desired, padding + // should be set to 1.0 + if (this.pad == 0) { + this.pad = 1.0; + } + if (this.padMax == 0) { + this.padMax = 1.0; + } + if (this.padMin == 0) { + this.padMin = 1.0; + } + if (this.padMax == null) { + this.padMax = (this.pad-1)/2 + 1; + } + if (this.padMin == null) { + this.padMin = (this.pad-1)/2 + 1; + } + // now that padMin and padMax are correctly set, reset pad in case user has supplied + // padMin and/or padMax + this.pad = this.padMax + this.padMin - 1; + if (this.min != null || this.max != null) { + this.autoscale = false; + } + // if not set, sync ticks for y axes but not x by default. + if (this.syncTicks == null && this.name.indexOf('y') > -1) { + this.syncTicks = true; + } + else if (this.syncTicks == null){ + this.syncTicks = false; + } + this.renderer.init.call(this, this.rendererOptions); + + }; + + Axis.prototype.draw = function(ctx, plot) { + // Memory Leaks patch + if (this.__ticks) { + this.__ticks = null; + } + + return this.renderer.draw.call(this, ctx, plot); + + }; + + Axis.prototype.set = function() { + this.renderer.set.call(this); + }; + + Axis.prototype.pack = function(pos, offsets) { + if (this.show) { + this.renderer.pack.call(this, pos, offsets); + } + // these properties should all be available now. + if (this._min == null) { + this._min = this.min; + this._max = this.max; + this._tickInterval = this.tickInterval; + this._numberTicks = this.numberTicks; + this.__ticks = this._ticks; + } + }; + + // reset the axis back to original values if it has been scaled, zoomed, etc. + Axis.prototype.reset = function() { + this.renderer.reset.call(this); + }; + + Axis.prototype.resetScale = function(opts) { + $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts); + this.resetDataBounds(); + }; + + Axis.prototype.resetDataBounds = function() { + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + var db = this._dataBounds; + db.min = null; + db.max = null; + var l, s, d; + // check for when to force min 0 on bar series plots. + var doforce = (this.show) ? true : false; + for (var i=0; i db.max) || db.max == null) { + db.max = d[j][0]; + } + } + else { + if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) { + db.min = d[j][minyidx]; + } + if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) { + db.max = d[j][maxyidx]; + } + } + } + + // Hack to not pad out bottom of bar plots unless user has specified a padding. + // every series will have a chance to set doforce to false. once it is set to + // false, it cannot be reset to true. + // If any series attached to axis is not a bar, wont force 0. + if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) { + doforce = false; + } + + else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) { + doforce = false; + } + + else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) { + if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') { + if (this._options.pad != null || this._options.padMin != null) { + doforce = false; + } + } + + else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) { + if (this._options.pad != null || this._options.padMin != null) { + doforce = false; + } + } + + } + } + } + + if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) { + this.padMin = 1.0; + this.forceTickAt0 = true; + } + }; + + /** + * Class: Legend + * Legend object. Cannot be instantiated directly, but created + * by the Plot object. Legend properties can be set or overridden by the + * options passed in from the user. + */ + function Legend(options) { + $.jqplot.ElemContainer.call(this); + // Group: Properties + + // prop: show + // Wether to display the legend on the graph. + this.show = false; + // prop: location + // Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w + this.location = 'ne'; + // prop: labels + // Array of labels to use. By default the renderer will look for labels on the series. + // Labels specified in this array will override labels specified on the series. + this.labels = []; + // prop: showLabels + // true to show the label text on the legend. + this.showLabels = true; + // prop: showSwatch + // true to show the color swatches on the legend. + this.showSwatches = true; + // prop: placement + // "insideGrid" places legend inside the grid area of the plot. + // "outsideGrid" places the legend outside the grid but inside the plot container, + // shrinking the grid to accomodate the legend. + // "inside" synonym for "insideGrid", + // "outside" places the legend ouside the grid area, but does not shrink the grid which + // can cause the legend to overflow the plot container. + this.placement = "insideGrid"; + // prop: xoffset + // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. + // properties or via CSS margin styling of the .jqplot-table-legend class. + this.xoffset = 0; + // prop: yoffset + // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. + // properties or via CSS margin styling of the .jqplot-table-legend class. + this.yoffset = 0; + // prop: border + // css spec for the border around the legend box. + this.border; + // prop: background + // css spec for the background of the legend box. + this.background; + // prop: textColor + // css color spec for the legend text. + this.textColor; + // prop: fontFamily + // css font-family spec for the legend text. + this.fontFamily; + // prop: fontSize + // css font-size spec for the legend text. + this.fontSize ; + // prop: rowSpacing + // css padding-top spec for the rows in the legend. + this.rowSpacing = '0.5em'; + // renderer + // A class that will create a DOM object for the legend, + // see <$.jqplot.TableLegendRenderer>. + this.renderer = $.jqplot.TableLegendRenderer; + // prop: rendererOptions + // renderer specific options passed to the renderer. + this.rendererOptions = {}; + // prop: predraw + // Wether to draw the legend before the series or not. + // Used with series specific legend renderers for pie, donut, mekko charts, etc. + this.preDraw = false; + // prop: marginTop + // CSS margin for the legend DOM element. This will set an element + // CSS style for the margin which will override any style sheet setting. + // The default will be taken from the stylesheet. + this.marginTop = null; + // prop: marginRight + // CSS margin for the legend DOM element. This will set an element + // CSS style for the margin which will override any style sheet setting. + // The default will be taken from the stylesheet. + this.marginRight = null; + // prop: marginBottom + // CSS margin for the legend DOM element. This will set an element + // CSS style for the margin which will override any style sheet setting. + // The default will be taken from the stylesheet. + this.marginBottom = null; + // prop: marginLeft + // CSS margin for the legend DOM element. This will set an element + // CSS style for the margin which will override any style sheet setting. + // The default will be taken from the stylesheet. + this.marginLeft = null; + // prop: escapeHtml + // True to escape special characters with their html entity equivalents + // in legend text. "<" becomes < and so on, so html tags are not rendered. + this.escapeHtml = false; + this._series = []; + + $.extend(true, this, options); + } + + Legend.prototype = new $.jqplot.ElemContainer(); + Legend.prototype.constructor = Legend; + + Legend.prototype.setOptions = function(options) { + $.extend(true, this, options); + + // Try to emulate deprecated behaviour + // if user has specified xoffset or yoffset, copy these to + // the margin properties. + + if (this.placement == 'inside') { + this.placement = 'insideGrid'; + } + + if (this.xoffset >0) { + if (this.placement == 'insideGrid') { + switch (this.location) { + case 'nw': + case 'w': + case 'sw': + if (this.marginLeft == null) { + this.marginLeft = this.xoffset + 'px'; + } + this.marginRight = '0px'; + break; + case 'ne': + case 'e': + case 'se': + default: + if (this.marginRight == null) { + this.marginRight = this.xoffset + 'px'; + } + this.marginLeft = '0px'; + break; + } + } + else if (this.placement == 'outside') { + switch (this.location) { + case 'nw': + case 'w': + case 'sw': + if (this.marginRight == null) { + this.marginRight = this.xoffset + 'px'; + } + this.marginLeft = '0px'; + break; + case 'ne': + case 'e': + case 'se': + default: + if (this.marginLeft == null) { + this.marginLeft = this.xoffset + 'px'; + } + this.marginRight = '0px'; + break; + } + } + this.xoffset = 0; + } + + if (this.yoffset >0) { + if (this.placement == 'outside') { + switch (this.location) { + case 'sw': + case 's': + case 'se': + if (this.marginTop == null) { + this.marginTop = this.yoffset + 'px'; + } + this.marginBottom = '0px'; + break; + case 'ne': + case 'n': + case 'nw': + default: + if (this.marginBottom == null) { + this.marginBottom = this.yoffset + 'px'; + } + this.marginTop = '0px'; + break; + } + } + else if (this.placement == 'insideGrid') { + switch (this.location) { + case 'sw': + case 's': + case 'se': + if (this.marginBottom == null) { + this.marginBottom = this.yoffset + 'px'; + } + this.marginTop = '0px'; + break; + case 'ne': + case 'n': + case 'nw': + default: + if (this.marginTop == null) { + this.marginTop = this.yoffset + 'px'; + } + this.marginBottom = '0px'; + break; + } + } + this.yoffset = 0; + } + + // TO-DO: + // Handle case where offsets are < 0. + // + }; + + Legend.prototype.init = function() { + if ($.isFunction(this.renderer)) { + this.renderer = new this.renderer(); + } + this.renderer.init.call(this, this.rendererOptions); + }; + + Legend.prototype.draw = function(offsets, plot) { + for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){ + $.jqplot.preDrawLegendHooks[i].call(this, offsets); + } + return this.renderer.draw.call(this, offsets, plot); + }; + + Legend.prototype.pack = function(offsets) { + this.renderer.pack.call(this, offsets); + }; + + /** + * Class: Title + * Plot Title object. Cannot be instantiated directly, but created + * by the Plot object. Title properties can be set or overridden by the + * options passed in from the user. + * + * Parameters: + * text - text of the title. + */ + function Title(text) { + $.jqplot.ElemContainer.call(this); + // Group: Properties + + // prop: text + // text of the title; + this.text = text; + // prop: show + // whether or not to show the title + this.show = true; + // prop: fontFamily + // css font-family spec for the text. + this.fontFamily; + // prop: fontSize + // css font-size spec for the text. + this.fontSize ; + // prop: textAlign + // css text-align spec for the text. + this.textAlign; + // prop: textColor + // css color spec for the text. + this.textColor; + // prop: renderer + // A class for creating a DOM element for the title, + // see <$.jqplot.DivTitleRenderer>. + this.renderer = $.jqplot.DivTitleRenderer; + // prop: rendererOptions + // renderer specific options passed to the renderer. + this.rendererOptions = {}; + // prop: escapeHtml + // True to escape special characters with their html entity equivalents + // in title text. "<" becomes < and so on, so html tags are not rendered. + this.escapeHtml = false; + } + + Title.prototype = new $.jqplot.ElemContainer(); + Title.prototype.constructor = Title; + + Title.prototype.init = function() { + if ($.isFunction(this.renderer)) { + this.renderer = new this.renderer(); + } + this.renderer.init.call(this, this.rendererOptions); + }; + + Title.prototype.draw = function(width) { + return this.renderer.draw.call(this, width); + }; + + Title.prototype.pack = function() { + this.renderer.pack.call(this); + }; + + + /** + * Class: Series + * An individual data series object. Cannot be instantiated directly, but created + * by the Plot object. Series properties can be set or overridden by the + * options passed in from the user. + */ + function Series(options) { + options = options || {}; + $.jqplot.ElemContainer.call(this); + // Group: Properties + // Properties will be assigned from a series array at the top level of the + // options. If you had two series and wanted to change the color and line + // width of the first and set the second to use the secondary y axis with + // no shadow and supply custom labels for each: + // > { + // > series:[ + // > {color: '#ff4466', lineWidth: 5, label:'good line'}, + // > {yaxis: 'y2axis', shadow: false, label:'bad line'} + // > ] + // > } + + // prop: show + // whether or not to draw the series. + this.show = true; + // prop: xaxis + // which x axis to use with this series, either 'xaxis' or 'x2axis'. + this.xaxis = 'xaxis'; + this._xaxis; + // prop: yaxis + // which y axis to use with this series, either 'yaxis' or 'y2axis'. + this.yaxis = 'yaxis'; + this._yaxis; + this.gridBorderWidth = 2.0; + // prop: renderer + // A class of a renderer which will draw the series, + // see <$.jqplot.LineRenderer>. + this.renderer = $.jqplot.LineRenderer; + // prop: rendererOptions + // Options to pass on to the renderer. + this.rendererOptions = {}; + this.data = []; + this.gridData = []; + // prop: label + // Line label to use in the legend. + this.label = ''; + // prop: showLabel + // true to show label for this series in the legend. + this.showLabel = true; + // prop: color + // css color spec for the series + this.color; + // prop: negativeColor + // css color spec used for filled (area) plots that are filled to zero and + // the "useNegativeColors" option is true. + this.negativeColor; + // prop: lineWidth + // width of the line in pixels. May have different meanings depending on renderer. + this.lineWidth = 2.5; + // prop: lineJoin + // Canvas lineJoin style between segments of series. + this.lineJoin = 'round'; + // prop: lineCap + // Canvas lineCap style at ends of line. + this.lineCap = 'round'; + // prop: linePattern + // line pattern 'dashed', 'dotted', 'solid', some combination + // of '-' and '.' characters such as '.-.' or a numerical array like + // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, + // [1, 10, 20, 10] to draw a dot-dash line, and so on. + this.linePattern = 'solid'; + this.shadow = true; + // prop: shadowAngle + // Shadow angle in degrees + this.shadowAngle = 45; + // prop: shadowOffset + // Shadow offset from line in pixels + this.shadowOffset = 1.25; + // prop: shadowDepth + // Number of times shadow is stroked, each stroke offset shadowOffset from the last. + this.shadowDepth = 3; + // prop: shadowAlpha + // Alpha channel transparency of shadow. 0 = transparent. + this.shadowAlpha = '0.1'; + // prop: breakOnNull + // Wether line segments should be be broken at null value. + // False will join point on either side of line. + this.breakOnNull = false; + // prop: markerRenderer + // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points, + // see <$.jqplot.MarkerRenderer>. + this.markerRenderer = $.jqplot.MarkerRenderer; + // prop: markerOptions + // renderer specific options to pass to the markerRenderer, + // see <$.jqplot.MarkerRenderer>. + this.markerOptions = {}; + // prop: showLine + // whether to actually draw the line or not. Series will still be renderered, even if no line is drawn. + this.showLine = true; + // prop: showMarker + // whether or not to show the markers at the data points. + this.showMarker = true; + // prop: index + // 0 based index of this series in the plot series array. + this.index; + // prop: fill + // true or false, whether to fill under lines or in bars. + // May not be implemented in all renderers. + this.fill = false; + // prop: fillColor + // CSS color spec to use for fill under line. Defaults to line color. + this.fillColor; + // prop: fillAlpha + // Alpha transparency to apply to the fill under the line. + // Use this to adjust alpha separate from fill color. + this.fillAlpha; + // prop: fillAndStroke + // If true will stroke the line (with color this.color) as well as fill under it. + // Applies only when fill is true. + this.fillAndStroke = false; + // prop: disableStack + // true to not stack this series with other series in the plot. + // To render properly, non-stacked series must come after any stacked series + // in the plot's data series array. So, the plot's data series array would look like: + // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...] + // disableStack will put a gap in the stacking order of series, and subsequent + // stacked series will not fill down through the non-stacked series and will + // most likely not stack properly on top of the non-stacked series. + this.disableStack = false; + // _stack is set by the Plot if the plot is a stacked chart. + // will stack lines or bars on top of one another to build a "mountain" style chart. + // May not be implemented in all renderers. + this._stack = false; + // prop: neighborThreshold + // how close or far (in pixels) the cursor must be from a point marker to detect the point. + this.neighborThreshold = 4; + // prop: fillToZero + // true will force bar and filled series to fill toward zero on the fill Axis. + this.fillToZero = false; + // prop: fillToValue + // fill a filled series to this value on the fill axis. + // Works in conjunction with fillToZero, so that must be true. + this.fillToValue = 0; + // prop: fillAxis + // Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true. + // 'y' means fill up/down to 0 on the y axis for this series. + this.fillAxis = 'y'; + // prop: useNegativeColors + // true to color negative values differently in filled and bar charts. + this.useNegativeColors = true; + this._stackData = []; + // _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If + // stacked, _plotData is accumulation of stacking data. + this._plotData = []; + // _plotValues hold the individual x and y values that will be plotted for this series. + this._plotValues = {x:[], y:[]}; + // statistics about the intervals between data points. Used for auto scaling. + this._intervals = {x:{}, y:{}}; + // data from the previous series, for stacked charts. + this._prevPlotData = []; + this._prevGridData = []; + this._stackAxis = 'y'; + this._primaryAxis = '_xaxis'; + // give each series a canvas to draw on. This should allow for redrawing speedups. + this.canvas = new $.jqplot.GenericCanvas(); + this.shadowCanvas = new $.jqplot.GenericCanvas(); + this.plugins = {}; + // sum of y values in this series. + this._sumy = 0; + this._sumx = 0; + this._type = ''; + } + + Series.prototype = new $.jqplot.ElemContainer(); + Series.prototype.constructor = Series; + + Series.prototype.init = function(index, gridbw, plot) { + // weed out any null values in the data. + this.index = index; + this.gridBorderWidth = gridbw; + var d = this.data; + var temp = [], i, l; + for (i=0, l=d.length; i. + this.renderer = $.jqplot.CanvasGridRenderer; + // prop: rendererOptions + // Options to pass on to the renderer, + // see <$.jqplot.CanvasGridRenderer>. + this.rendererOptions = {}; + this._offsets = {top:null, bottom:null, left:null, right:null}; + } + + Grid.prototype = new $.jqplot.ElemContainer(); + Grid.prototype.constructor = Grid; + + Grid.prototype.init = function() { + if ($.isFunction(this.renderer)) { + this.renderer = new this.renderer(); + } + this.renderer.init.call(this, this.rendererOptions); + }; + + Grid.prototype.createElement = function(offsets,plot) { + this._offsets = offsets; + return this.renderer.createElement.call(this, plot); + }; + + Grid.prototype.draw = function() { + this.renderer.draw.call(this); + }; + + $.jqplot.GenericCanvas = function() { + $.jqplot.ElemContainer.call(this); + this._ctx; + }; + + $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer(); + $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas; + + $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) { + this._offsets = offsets; + var klass = 'jqplot'; + if (clss != undefined) { + klass = clss; + } + var elem; + + elem = plot.canvasManager.getCanvas(); + + // if new plotDimensions supplied, use them. + if (plotDimensions != null) { + this._plotDimensions = plotDimensions; + } + + elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right; + elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom; + this._elem = $(elem); + this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top }); + + this._elem.addClass(klass); + + elem = plot.canvasManager.initCanvas(elem); + + elem = null; + return this._elem; + }; + + $.jqplot.GenericCanvas.prototype.setContext = function() { + this._ctx = this._elem.get(0).getContext("2d"); + return this._ctx; + }; + + // Memory Leaks patch + $.jqplot.GenericCanvas.prototype.resetCanvas = function() { + if (this._elem) { + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); + } + + //this._elem.remove(); + this._elem.emptyForce(); + } + + this._ctx = null; + }; + + $.jqplot.HooksManager = function () { + this.hooks =[]; + this.args = []; + }; + + $.jqplot.HooksManager.prototype.addOnce = function(fn, args) { + args = args || []; + var havehook = false; + for (var i=0, l=this.hooks.length; i { + // > axesDefaults:{min:0}, + // > series:[{color:'#6633dd'}], + // > title: 'A Plot' + // > } + // + + // prop: animate + // True to animate the series on initial plot draw (renderer dependent). + // Actual animation functionality must be supported in the renderer. + this.animate = false; + // prop: animateReplot + // True to animate series after a call to the replot() method. + // Use with caution! Replots can happen very frequently under + // certain circumstances (e.g. resizing, dragging points) and + // animation in these situations can cause problems. + this.animateReplot = false; + // prop: axes + // up to 4 axes are supported, each with its own options, + // See for axis specific options. + this.axes = {xaxis: new Axis('xaxis'), yaxis: new Axis('yaxis'), x2axis: new Axis('x2axis'), y2axis: new Axis('y2axis'), y3axis: new Axis('y3axis'), y4axis: new Axis('y4axis'), y5axis: new Axis('y5axis'), y6axis: new Axis('y6axis'), y7axis: new Axis('y7axis'), y8axis: new Axis('y8axis'), y9axis: new Axis('y9axis'), yMidAxis: new Axis('yMidAxis')}; + this.baseCanvas = new $.jqplot.GenericCanvas(); + // true to intercept right click events and fire a 'jqplotRightClick' event. + // this will also block the context menu. + this.captureRightClick = false; + // prop: data + // user's data. Data should *NOT* be specified in the options object, + // but be passed in as the second argument to the $.jqplot() function. + // The data property is described here soley for reference. + // The data should be in the form of an array of 2D or 1D arrays like + // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ]. + this.data = []; + // prop: dataRenderer + // A callable which can be used to preprocess data passed into the plot. + // Will be called with 3 arguments: the plot data, a reference to the plot, + // and the value of dataRendererOptions. + this.dataRenderer; + // prop: dataRendererOptions + // Options that will be passed to the dataRenderer. + // Can be of any type. + this.dataRendererOptions; + this.defaults = { + // prop: axesDefaults + // default options that will be applied to all axes. + // see for axes options. + axesDefaults: {}, + axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}}, + // prop: seriesDefaults + // default options that will be applied to all series. + // see for series options. + seriesDefaults: {}, + series:[] + }; + // prop: defaultAxisStart + // 1-D data series are internally converted into 2-D [x,y] data point arrays + // by jqPlot. This is the default starting value for the missing x or y value. + // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...]) + // starting at this value. + this.defaultAxisStart = 1; + // this.doCustomEventBinding = true; + // prop: drawIfHidden + // True to execute the draw method even if the plot target is hidden. + // Generally, this should be false. Most plot elements will not be sized/ + // positioned correclty if renderered into a hidden container. To render into + // a hidden container, call the replot method when the container is shown. + this.drawIfHidden = false; + this.eventCanvas = new $.jqplot.GenericCanvas(); + // prop: fillBetween + // Fill between 2 line series in a plot. + // Options object: + // { + // series1: first index (0 based) of series in fill + // series2: second index (0 based) of series in fill + // color: color of fill [default fillColor of series1] + // baseSeries: fill will be drawn below this series (0 based index) + // fill: false to turn off fill [default true]. + // } + this.fillBetween = { + series1: null, + series2: null, + color: null, + baseSeries: 0, + fill: true + }; + // prop; fontFamily + // css spec for the font-family attribute. Default for the entire plot. + this.fontFamily; + // prop: fontSize + // css spec for the font-size attribute. Default for the entire plot. + this.fontSize; + // prop: grid + // See for grid specific options. + this.grid = new Grid(); + // prop: legend + // see <$.jqplot.TableLegendRenderer> + this.legend = new Legend(); + // prop: noDataIndicator + // Options to set up a mock plot with a data loading indicator if no data is specified. + this.noDataIndicator = { + show: false, + indicator: 'Loading Data...', + axes: { + xaxis: { + min: 0, + max: 10, + tickInterval: 2, + show: true + }, + yaxis: { + min: 0, + max: 12, + tickInterval: 3, + show: true + } + } + }; + // prop: negativeSeriesColors + // colors to use for portions of the line below zero. + this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors; + // container to hold all of the merged options. Convienence for plugins. + this.options = {}; + this.previousSeriesStack = []; + // Namespace to hold plugins. Generally non-renderer plugins add themselves to here. + this.plugins = {}; + // prop: series + // Array of series object options. + // see for series specific options. + this.series = []; + // array of series indices. Keep track of order + // which series canvases are displayed, lowest + // to highest, back to front. + this.seriesStack = []; + // prop: seriesColors + // Ann array of CSS color specifications that will be applied, in order, + // to the series in the plot. Colors will wrap around so, if their + // are more series than colors, colors will be reused starting at the + // beginning. For pie charts, this specifies the colors of the slices. + this.seriesColors = $.jqplot.config.defaultColors; + // prop: sortData + // false to not sort the data passed in by the user. + // Many bar, stacked and other graphs as well as many plugins depend on + // having sorted data. + this.sortData = true; + // prop: stackSeries + // true or false, creates a stack or "mountain" plot. + // Not all series renderers may implement this option. + this.stackSeries = false; + // a shortcut for axis syncTicks options. Not implemented yet. + this.syncXTicks = true; + // a shortcut for axis syncTicks options. Not implemented yet. + this.syncYTicks = true; + // the jquery object for the dom target. + this.target = null; + // The id of the dom element to render the plot into + this.targetId = null; + // prop textColor + // css spec for the css color attribute. Default for the entire plot. + this.textColor; + // prop: title + // Title object. See for specific options. As a shortcut, you + // can specify the title option as just a string like: title: 'My Plot' + // and this will create a new title object with the specified text. + this.title = new Title(); + // Count how many times the draw method has been called while the plot is visible. + // Mostly used to test if plot has never been dran (=0), has been successfully drawn + // into a visible container once (=1) or draw more than once into a visible container. + // Can use this in tests to see if plot has been visibly drawn at least one time. + // After plot has been visibly drawn once, it generally doesn't need redrawing if its + // container is hidden and shown. + this._drawCount = 0; + // sum of y values for all series in plot. + // used in mekko chart. + this._sumy = 0; + this._sumx = 0; + // array to hold the cumulative stacked series data. + // used to ajust the individual series data, which won't have access to other + // series data. + this._stackData = []; + // array that holds the data to be plotted. This will be the series data + // merged with the the appropriate data from _stackData according to the stackAxis. + this._plotData = []; + this._width = null; + this._height = null; + this._plotDimensions = {height:null, width:null}; + this._gridPadding = {top:null, right:null, bottom:null, left:null}; + this._defaultGridPadding = {top:10, right:10, bottom:23, left:10}; + + this._addDomReference = $.jqplot.config.addDomReference; + + this.preInitHooks = new $.jqplot.HooksManager(); + this.postInitHooks = new $.jqplot.HooksManager(); + this.preParseOptionsHooks = new $.jqplot.HooksManager(); + this.postParseOptionsHooks = new $.jqplot.HooksManager(); + this.preDrawHooks = new $.jqplot.HooksManager(); + this.postDrawHooks = new $.jqplot.HooksManager(); + this.preDrawSeriesHooks = new $.jqplot.HooksManager(); + this.postDrawSeriesHooks = new $.jqplot.HooksManager(); + this.preDrawLegendHooks = new $.jqplot.HooksManager(); + this.addLegendRowHooks = new $.jqplot.HooksManager(); + this.preSeriesInitHooks = new $.jqplot.HooksManager(); + this.postSeriesInitHooks = new $.jqplot.HooksManager(); + this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager(); + this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager(); + this.eventListenerHooks = new $.jqplot.EventListenerManager(); + this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager(); + this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager(); + + this.colorGenerator = new $.jqplot.ColorGenerator(); + this.negativeColorGenerator = new $.jqplot.ColorGenerator(); + + this.canvasManager = new $.jqplot.CanvasManager(); + + this.themeEngine = new $.jqplot.ThemeEngine(); + + var seriesColorsIndex = 0; + + // Group: methods + // + // method: init + // sets the plot target, checks data and applies user + // options to plot. + this.init = function(target, data, options) { + options = options || {}; + for (var i=0; i<$.jqplot.preInitHooks.length; i++) { + $.jqplot.preInitHooks[i].call(this, target, data, options); + } + + for (var i=0; i<this.preInitHooks.hooks.length; i++) { + this.preInitHooks.hooks[i].call(this, target, data, options); + } + + this.targetId = '#'+target; + this.target = $('#'+target); + + ////// + // Add a reference to plot + ////// + if (this._addDomReference) { + this.target.data('jqplot', this); + } + // remove any error class that may be stuck on target. + this.target.removeClass('jqplot-error'); + if (!this.target.get(0)) { + throw new Error("No plot target specified"); + } + + // make sure the target is positioned by some means and set css + if (this.target.css('position') == 'static') { + this.target.css('position', 'relative'); + } + if (!this.target.hasClass('jqplot-target')) { + this.target.addClass('jqplot-target'); + } + + // if no height or width specified, use a default. + if (!this.target.height()) { + var h; + if (options && options.height) { + h = parseInt(options.height, 10); + } + else if (this.target.attr('data-height')) { + h = parseInt(this.target.attr('data-height'), 10); + } + else { + h = parseInt($.jqplot.config.defaultHeight, 10); + } + this._height = h; + this.target.css('height', h+'px'); + } + else { + this._height = h = this.target.height(); + } + if (!this.target.width()) { + var w; + if (options && options.width) { + w = parseInt(options.width, 10); + } + else if (this.target.attr('data-width')) { + w = parseInt(this.target.attr('data-width'), 10); + } + else { + w = parseInt($.jqplot.config.defaultWidth, 10); + } + this._width = w; + this.target.css('width', w+'px'); + } + else { + this._width = w = this.target.width(); + } + + for (var i=0, l=_axisNames.length; i<l; i++) { + this.axes[_axisNames[i]] = new Axis(_axisNames[i]); + } + + this._plotDimensions.height = this._height; + this._plotDimensions.width = this._width; + this.grid._plotDimensions = this._plotDimensions; + this.title._plotDimensions = this._plotDimensions; + this.baseCanvas._plotDimensions = this._plotDimensions; + this.eventCanvas._plotDimensions = this._plotDimensions; + this.legend._plotDimensions = this._plotDimensions; + if (this._height <=0 || this._width <=0 || !this._height || !this._width) { + throw new Error("Canvas dimension not set"); + } + + if (options.dataRenderer && $.isFunction(options.dataRenderer)) { + if (options.dataRendererOptions) { + this.dataRendererOptions = options.dataRendererOptions; + } + this.dataRenderer = options.dataRenderer; + data = this.dataRenderer(data, this, this.dataRendererOptions); + } + + if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) { + $.extend(true, this.noDataIndicator, options.noDataIndicator); + } + + if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) { + + if (this.noDataIndicator.show == false) { + throw new Error("No data specified"); + } + + else { + // have to be descructive here in order for plot to not try and render series. + // This means that $.jqplot() will have to be called again when there is data. + //delete options.series; + + for (var ax in this.noDataIndicator.axes) { + for (var prop in this.noDataIndicator.axes[ax]) { + this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop]; + } + } + + this.postDrawHooks.add(function() { + var eh = this.eventCanvas.getHeight(); + var ew = this.eventCanvas.getWidth(); + var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>'); + this.target.append(temp); + temp.height(eh); + temp.width(ew); + temp.css('top', this.eventCanvas._offsets.top); + temp.css('left', this.eventCanvas._offsets.left); + + var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>'); + temp.append(temp2); + temp2.html(this.noDataIndicator.indicator); + var th = temp2.height(); + var tw = temp2.width(); + temp2.height(th); + temp2.width(tw); + temp2.css('top', (eh - th)/2 + 'px'); + }); + + } + } + + // make a copy of the data + this.data = $.extend(true, [], data); + + this.parseOptions(options); + + if (this.textColor) { + this.target.css('color', this.textColor); + } + if (this.fontFamily) { + this.target.css('font-family', this.fontFamily); + } + if (this.fontSize) { + this.target.css('font-size', this.fontSize); + } + + this.title.init(); + this.legend.init(); + this._sumy = 0; + this._sumx = 0; + this.computePlotData(); + for (var i=0; i<this.series.length; i++) { + // set default stacking order for series canvases + this.seriesStack.push(i); + this.previousSeriesStack.push(i); + this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; + this.series[i].canvas._plotDimensions = this._plotDimensions; + for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { + $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { + this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + // this.populatePlotData(this.series[i], i); + this.series[i]._plotDimensions = this._plotDimensions; + this.series[i].init(i, this.grid.borderWidth, this); + for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { + $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { + this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + this._sumy += this.series[i]._sumy; + this._sumx += this.series[i]._sumx; + } + + var name, + axis; + for (var i=0, l=_axisNames.length; i<l; i++) { + name = _axisNames[i]; + axis = this.axes[name]; + axis._plotDimensions = this._plotDimensions; + axis.init(); + if (this.axes[name].borderColor == null) { + if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { + axis.borderColor = axis._series[0].color; + } + else { + axis.borderColor = this.grid.borderColor; + } + } + } + + if (this.sortData) { + sortData(this.series); + } + this.grid.init(); + this.grid._axes = this.axes; + + this.legend._series = this.series; + + for (var i=0; i<$.jqplot.postInitHooks.length; i++) { + $.jqplot.postInitHooks[i].call(this, target, this.data, options); + } + + for (var i=0; i<this.postInitHooks.hooks.length; i++) { + this.postInitHooks.hooks[i].call(this, target, this.data, options); + } + }; + + // method: resetAxesScale + // Reset the specified axes min, max, numberTicks and tickInterval properties to null + // or reset these properties on all axes if no list of axes is provided. + // + // Parameters: + // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset. + this.resetAxesScale = function(axes, options) { + var opts = options || {}; + var ax = axes || this.axes; + if (ax === true) { + ax = this.axes; + } + if ($.isArray(ax)) { + for (var i = 0; i < ax.length; i++) { + this.axes[ax[i]].resetScale(opts[ax[i]]); + } + } + else if (typeof(ax) === 'object') { + for (var name in ax) { + this.axes[name].resetScale(opts[name]); + } + } + }; + // method: reInitialize + // reinitialize plot for replotting. + // not called directly. + this.reInitialize = function (data, opts) { + // Plot should be visible and have a height and width. + // If plot doesn't have height and width for some + // reason, set it by other means. Plot must not have + // a display:none attribute, however. + + var options = $.extend(true, {}, this.options, opts); + + var target = this.targetId.substr(1); + var tdata = (data == null) ? this.data : data; + + for (var i=0; i<$.jqplot.preInitHooks.length; i++) { + $.jqplot.preInitHooks[i].call(this, target, tdata, options); + } + + for (var i=0; i<this.preInitHooks.hooks.length; i++) { + this.preInitHooks.hooks[i].call(this, target, tdata, options); + } + + this._height = this.target.height(); + this._width = this.target.width(); + + if (this._height <=0 || this._width <=0 || !this._height || !this._width) { + throw new Error("Target dimension not set"); + } + + this._plotDimensions.height = this._height; + this._plotDimensions.width = this._width; + this.grid._plotDimensions = this._plotDimensions; + this.title._plotDimensions = this._plotDimensions; + this.baseCanvas._plotDimensions = this._plotDimensions; + this.eventCanvas._plotDimensions = this._plotDimensions; + this.legend._plotDimensions = this._plotDimensions; + + var name, + t, + j, + axis; + + for (var i=0, l=_axisNames.length; i<l; i++) { + name = _axisNames[i]; + axis = this.axes[name]; + + // Memory Leaks patch : clear ticks elements + t = axis._ticks; + for (var j = 0, tlen = t.length; j < tlen; j++) { + var el = t[j]._elem; + if (el) { + // if canvas renderer + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + window.G_vmlCanvasManager.uninitElement(el.get(0)); + } + el.emptyForce(); + el = null; + t._elem = null; + } + } + t = null; + + delete axis.ticks; + delete axis._ticks; + this.axes[name] = new Axis(name); + this.axes[name]._plotWidth = this._width; + this.axes[name]._plotHeight = this._height; + } + + if (data) { + if (options.dataRenderer && $.isFunction(options.dataRenderer)) { + if (options.dataRendererOptions) { + this.dataRendererOptions = options.dataRendererOptions; + } + this.dataRenderer = options.dataRenderer; + data = this.dataRenderer(data, this, this.dataRendererOptions); + } + + // make a copy of the data + this.data = $.extend(true, [], data); + } + + if (opts) { + this.parseOptions(options); + } + + this.title._plotWidth = this._width; + + if (this.textColor) { + this.target.css('color', this.textColor); + } + if (this.fontFamily) { + this.target.css('font-family', this.fontFamily); + } + if (this.fontSize) { + this.target.css('font-size', this.fontSize); + } + + this.title.init(); + this.legend.init(); + this._sumy = 0; + this._sumx = 0; + + this.seriesStack = []; + this.previousSeriesStack = []; + + this.computePlotData(); + for (var i=0, l=this.series.length; i<l; i++) { + // set default stacking order for series canvases + this.seriesStack.push(i); + this.previousSeriesStack.push(i); + this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; + this.series[i].canvas._plotDimensions = this._plotDimensions; + for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { + $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { + this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + // this.populatePlotData(this.series[i], i); + this.series[i]._plotDimensions = this._plotDimensions; + this.series[i].init(i, this.grid.borderWidth, this); + for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { + $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { + this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); + } + this._sumy += this.series[i]._sumy; + this._sumx += this.series[i]._sumx; + } + + for (var i=0, l=_axisNames.length; i<l; i++) { + name = _axisNames[i]; + axis = this.axes[name]; + + axis._plotDimensions = this._plotDimensions; + axis.init(); + if (axis.borderColor == null) { + if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { + axis.borderColor = axis._series[0].color; + } + else { + axis.borderColor = this.grid.borderColor; + } + } + } + + if (this.sortData) { + sortData(this.series); + } + this.grid.init(); + this.grid._axes = this.axes; + + this.legend._series = this.series; + + for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) { + $.jqplot.postInitHooks[i].call(this, target, this.data, options); + } + + for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) { + this.postInitHooks.hooks[i].call(this, target, this.data, options); + } + }; + + + + // method: quickInit + // + // Quick reinitialization plot for replotting. + // Does not parse options ore recreate axes and series. + // not called directly. + this.quickInit = function () { + // Plot should be visible and have a height and width. + // If plot doesn't have height and width for some + // reason, set it by other means. Plot must not have + // a display:none attribute, however. + + this._height = this.target.height(); + this._width = this.target.width(); + + if (this._height <=0 || this._width <=0 || !this._height || !this._width) { + throw new Error("Target dimension not set"); + } + + this._plotDimensions.height = this._height; + this._plotDimensions.width = this._width; + this.grid._plotDimensions = this._plotDimensions; + this.title._plotDimensions = this._plotDimensions; + this.baseCanvas._plotDimensions = this._plotDimensions; + this.eventCanvas._plotDimensions = this._plotDimensions; + this.legend._plotDimensions = this._plotDimensions; + + for (var n in this.axes) { + this.axes[n]._plotWidth = this._width; + this.axes[n]._plotHeight = this._height; + } + + this.title._plotWidth = this._width; + + if (this.textColor) { + this.target.css('color', this.textColor); + } + if (this.fontFamily) { + this.target.css('font-family', this.fontFamily); + } + if (this.fontSize) { + this.target.css('font-size', this.fontSize); + } + + this._sumy = 0; + this._sumx = 0; + this.computePlotData(); + for (var i=0; i<this.series.length; i++) { + // this.populatePlotData(this.series[i], i); + if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) { + this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this); + } + this.series[i]._plotDimensions = this._plotDimensions; + this.series[i].canvas._plotDimensions = this._plotDimensions; + //this.series[i].init(i, this.grid.borderWidth); + this._sumy += this.series[i]._sumy; + this._sumx += this.series[i]._sumx; + } + + var name; + + for (var j=0; j<12; j++) { + name = _axisNames[j]; + // Memory Leaks patch : clear ticks elements + var t = this.axes[name]._ticks; + for (var i = 0; i < t.length; i++) { + var el = t[i]._elem; + if (el) { + // if canvas renderer + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + window.G_vmlCanvasManager.uninitElement(el.get(0)); + } + el.emptyForce(); + el = null; + t._elem = null; + } + } + t = null; + + this.axes[name]._plotDimensions = this._plotDimensions; + this.axes[name]._ticks = []; + // this.axes[name].renderer.init.call(this.axes[name], {}); + } + + if (this.sortData) { + sortData(this.series); + } + + this.grid._axes = this.axes; + + this.legend._series = this.series; + }; + + // sort the series data in increasing order. + function sortData(series) { + var d, sd, pd, ppd, ret; + for (var i=0; i<series.length; i++) { + var check; + var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData]; + for (var n=0; n<4; n++) { + check = true; + d = bat[n]; + if (series[i]._stackAxis == 'x') { + for (var j = 0; j < d.length; j++) { + if (typeof(d[j][1]) != "number") { + check = false; + break; + } + } + if (check) { + d.sort(function(a,b) { return a[1] - b[1]; }); + } + } + else { + for (var j = 0; j < d.length; j++) { + if (typeof(d[j][0]) != "number") { + check = false; + break; + } + } + if (check) { + d.sort(function(a,b) { return a[0] - b[0]; }); + } + } + } + + } + } + + this.computePlotData = function() { + this._plotData = []; + this._stackData = []; + var series, + index, + l; + + + for (index=0, l=this.series.length; index<l; index++) { + series = this.series[index]; + this._plotData.push([]); + this._stackData.push([]); + var cd = series.data; + this._plotData[index] = $.extend(true, [], cd); + this._stackData[index] = $.extend(true, [], cd); + series._plotData = this._plotData[index]; + series._stackData = this._stackData[index]; + var plotValues = {x:[], y:[]}; + + if (this.stackSeries && !series.disableStack) { + series._stack = true; + /////////////////////////// + // have to check for nulls + /////////////////////////// + var sidx = (series._stackAxis === 'x') ? 0 : 1; + + for (var k=0, cdl=cd.length; k<cdl; k++) { + var temp = cd[k][sidx]; + if (temp == null) { + temp = 0; + } + this._plotData[index][k][sidx] = temp; + this._stackData[index][k][sidx] = temp; + + if (index > 0) { + for (var j=index; j--;) { + var prevval = this._plotData[j][k][sidx]; + // only need to sum up the stack axis column of data + // and only sum if it is of same sign. + // if previous series isn't same sign, keep looking + // at earlier series untill we find one of same sign. + if (temp * prevval >= 0) { + this._plotData[index][k][sidx] += prevval; + this._stackData[index][k][sidx] += prevval; + break; + } + } + } + } + + } + else { + for (var i=0; i<series.data.length; i++) { + plotValues.x.push(series.data[i][0]); + plotValues.y.push(series.data[i][1]); + } + this._stackData.push(series.data); + this.series[index]._stackData = series.data; + this._plotData.push(series.data); + series._plotData = series.data; + series._plotValues = plotValues; + } + if (index>0) { + series._prevPlotData = this.series[index-1]._plotData; + } + series._sumy = 0; + series._sumx = 0; + for (i=series.data.length-1; i>-1; i--) { + series._sumy += series.data[i][1]; + series._sumx += series.data[i][0]; + } + } + + }; + + // populate the _stackData and _plotData arrays for the plot and the series. + this.populatePlotData = function(series, index) { + // if a stacked chart, compute the stacked data + this._plotData = []; + this._stackData = []; + series._stackData = []; + series._plotData = []; + var plotValues = {x:[], y:[]}; + if (this.stackSeries && !series.disableStack) { + series._stack = true; + var sidx = (series._stackAxis === 'x') ? 0 : 1; + // var idx = sidx ? 0 : 1; + // push the current data into stackData + //this._stackData.push(this.series[i].data); + var temp = $.extend(true, [], series.data); + // create the data that will be plotted for this series + var plotdata = $.extend(true, [], series.data); + var tempx, tempy, dval, stackval, comparator; + // for first series, nothing to add to stackData. + for (var j=0; j<index; j++) { + var cd = this.series[j].data; + for (var k=0; k<cd.length; k++) { + dval = cd[k]; + tempx = (dval[0] != null) ? dval[0] : 0; + tempy = (dval[1] != null) ? dval[1] : 0; + temp[k][0] += tempx; + temp[k][1] += tempy; + stackval = (sidx) ? tempy : tempx; + // only need to sum up the stack axis column of data + // and only sum if it is of same sign. + if (series.data[k][sidx] * stackval >= 0) { + plotdata[k][sidx] += stackval; + } + } + } + for (var i=0; i<plotdata.length; i++) { + plotValues.x.push(plotdata[i][0]); + plotValues.y.push(plotdata[i][1]); + } + this._plotData.push(plotdata); + this._stackData.push(temp); + series._stackData = temp; + series._plotData = plotdata; + series._plotValues = plotValues; + } + else { + for (var i=0; i<series.data.length; i++) { + plotValues.x.push(series.data[i][0]); + plotValues.y.push(series.data[i][1]); + } + this._stackData.push(series.data); + this.series[index]._stackData = series.data; + this._plotData.push(series.data); + series._plotData = series.data; + series._plotValues = plotValues; + } + if (index>0) { + series._prevPlotData = this.series[index-1]._plotData; + } + series._sumy = 0; + series._sumx = 0; + for (i=series.data.length-1; i>-1; i--) { + series._sumy += series.data[i][1]; + series._sumx += series.data[i][0]; + } + }; + + // function to safely return colors from the color array and wrap around at the end. + this.getNextSeriesColor = (function(t) { + var idx = 0; + var sc = t.seriesColors; + + return function () { + if (idx < sc.length) { + return sc[idx++]; + } + else { + idx = 0; + return sc[idx++]; + } + }; + })(this); + + this.parseOptions = function(options){ + for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) { + this.preParseOptionsHooks.hooks[i].call(this, options); + } + for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) { + $.jqplot.preParseOptionsHooks[i].call(this, options); + } + this.options = $.extend(true, {}, this.defaults, options); + var opts = this.options; + this.animate = opts.animate; + this.animateReplot = opts.animateReplot; + this.stackSeries = opts.stackSeries; + if ($.isPlainObject(opts.fillBetween)) { + + var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], + tempi; + + for (var i=0, l=temp.length; i<l; i++) { + tempi = temp[i]; + if (opts.fillBetween[tempi] != null) { + this.fillBetween[tempi] = opts.fillBetween[tempi]; + } + } + } + + if (opts.seriesColors) { + this.seriesColors = opts.seriesColors; + } + if (opts.negativeSeriesColors) { + this.negativeSeriesColors = opts.negativeSeriesColors; + } + if (opts.captureRightClick) { + this.captureRightClick = opts.captureRightClick; + } + this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart; + this.colorGenerator.setColors(this.seriesColors); + this.negativeColorGenerator.setColors(this.negativeSeriesColors); + // var cg = new this.colorGenerator(this.seriesColors); + // var ncg = new this.colorGenerator(this.negativeSeriesColors); + // this._gridPadding = this.options.gridPadding; + $.extend(true, this._gridPadding, opts.gridPadding); + this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData; + for (var i=0; i<12; i++) { + var n = _axisNames[i]; + var axis = this.axes[n]; + axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]); + $.extend(true, axis, opts.axesDefaults, opts.axes[n]); + axis._plotWidth = this._width; + axis._plotHeight = this._height; + } + // if (this.data.length == 0) { + // this.data = []; + // for (var i=0; i<this.options.series.length; i++) { + // this.data.push(this.options.series.data); + // } + // } + + var normalizeData = function(data, dir, start) { + // return data as an array of point arrays, + // in form [[x1,y1...], [x2,y2...], ...] + var temp = []; + var i, l; + dir = dir || 'vertical'; + if (!$.isArray(data[0])) { + // we have a series of scalars. One line with just y values. + // turn the scalar list of data into a data array of form: + // [[1, data[0]], [2, data[1]], ...] + for (i=0, l=data.length; i<l; i++) { + if (dir == 'vertical') { + temp.push([start + i, data[i]]); + } + else { + temp.push([data[i], start+i]); + } + } + } + else { + // we have a properly formatted data series, copy it. + $.extend(true, temp, data); + } + return temp; + }; + + var colorIndex = 0; + this.series = []; + for (var i=0; i<this.data.length; i++) { + var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}}); + // pass in options in case something needs set prior to initialization. + var temp = new Series(sopts); + for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) { + $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); + } + for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) { + this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); + } + // Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to + // mess up preParseSeriesOptionsHooks at this point. + $.extend(true, temp, sopts); + var dir = 'vertical'; + if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') { + dir = 'horizontal'; + temp._stackAxis = 'x'; + temp._primaryAxis = '_yaxis'; + } + temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart); + switch (temp.xaxis) { + case 'xaxis': + temp._xaxis = this.axes.xaxis; + break; + case 'x2axis': + temp._xaxis = this.axes.x2axis; + break; + default: + break; + } + temp._yaxis = this.axes[temp.yaxis]; + temp._xaxis._series.push(temp); + temp._yaxis._series.push(temp); + if (temp.show) { + temp._xaxis.show = true; + temp._yaxis.show = true; + } + else { + if (temp._xaxis.scaleToHiddenSeries) { + temp._xaxis.show = true; + } + if (temp._yaxis.scaleToHiddenSeries) { + temp._yaxis.show = true; + } + } + + // // parse the renderer options and apply default colors if not provided + // if (!temp.color && temp.show != false) { + // temp.color = cg.next(); + // colorIndex = cg.getIndex() - 1;; + // } + // if (!temp.negativeColor && temp.show != false) { + // temp.negativeColor = ncg.get(colorIndex); + // ncg.setIndex(colorIndex); + // } + if (!temp.label) { + temp.label = 'Series '+ (i+1).toString(); + } + // temp.rendererOptions.show = temp.show; + // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions); + this.series.push(temp); + for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) { + $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); + } + for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) { + this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); + } + } + + // copy the grid and title options into this object. + $.extend(true, this.grid, this.options.grid); + // if axis border properties aren't set, set default. + for (var i=0, l=_axisNames.length; i<l; i++) { + var n = _axisNames[i]; + var axis = this.axes[n]; + if (axis.borderWidth == null) { + axis.borderWidth =this.grid.borderWidth; + } + } + + if (typeof this.options.title == 'string') { + this.title.text = this.options.title; + } + else if (typeof this.options.title == 'object') { + $.extend(true, this.title, this.options.title); + } + this.title._plotWidth = this._width; + this.legend.setOptions(this.options.legend); + + for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) { + $.jqplot.postParseOptionsHooks[i].call(this, options); + } + for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) { + this.postParseOptionsHooks.hooks[i].call(this, options); + } + }; + + // method: destroy + // Releases all resources occupied by the plot + this.destroy = function() { + this.canvasManager.freeAllCanvases(); + if (this.eventCanvas && this.eventCanvas._elem) { + this.eventCanvas._elem.unbind(); + } + // Couple of posts on Stack Overflow indicate that empty() doesn't + // always cear up the dom and release memory. Sometimes setting + // innerHTML property to null is needed. Particularly on IE, may + // have to directly set it to null, bypassing $. + this.target.empty(); + + this.target[0].innerHTML = ''; + }; + + // method: replot + // Does a reinitialization of the plot followed by + // a redraw. Method could be used to interactively + // change plot characteristics and then replot. + // + // Parameters: + // options - Options used for replotting. + // + // Properties: + // clear - false to not clear (empty) the plot container before replotting (default: true). + // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves. + // optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false). + this.replot = function(options) { + var opts = options || {}; + var data = opts.data || null; + var clear = (opts.clear === false) ? false : true; + var resetAxes = opts.resetAxes || false; + delete opts.data; + delete opts.clear; + delete opts.resetAxes; + + this.target.trigger('jqplotPreReplot'); + + if (clear) { + this.destroy(); + } + // if have data or other options, full reinit. + // otherwise, quickinit. + if (data || !$.isEmptyObject(opts)) { + this.reInitialize(data, opts); + } + else { + this.quickInit(); + } + + if (resetAxes) { + this.resetAxesScale(resetAxes, opts.axes); + } + this.draw(); + this.target.trigger('jqplotPostReplot'); + }; + + // method: redraw + // Empties the plot target div and redraws the plot. + // This enables plot data and properties to be changed + // and then to comletely clear the plot and redraw. + // redraw *will not* reinitialize any plot elements. + // That is, axes will not be autoscaled and defaults + // will not be reapplied to any plot elements. redraw + // is used primarily with zooming. + // + // Parameters: + // clear - false to not clear (empty) the plot container before redrawing (default: true). + this.redraw = function(clear) { + clear = (clear != null) ? clear : true; + this.target.trigger('jqplotPreRedraw'); + if (clear) { + this.canvasManager.freeAllCanvases(); + this.eventCanvas._elem.unbind(); + // Dont think I bind any events to the target, this shouldn't be necessary. + // It will remove user's events. + // this.target.unbind(); + this.target.empty(); + } + for (var ax in this.axes) { + this.axes[ax]._ticks = []; + } + this.computePlotData(); + // for (var i=0; i<this.series.length; i++) { + // this.populatePlotData(this.series[i], i); + // } + this._sumy = 0; + this._sumx = 0; + for (var i=0, tsl = this.series.length; i<tsl; i++) { + this._sumy += this.series[i]._sumy; + this._sumx += this.series[i]._sumx; + } + this.draw(); + this.target.trigger('jqplotPostRedraw'); + }; + + // method: draw + // Draws all elements of the plot into the container. + // Does not clear the container before drawing. + this.draw = function(){ + if (this.drawIfHidden || this.target.is(':visible')) { + this.target.trigger('jqplotPreDraw'); + var i, + j, + l, + tempseries; + for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) { + $.jqplot.preDrawHooks[i].call(this); + } + for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) { + this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]); + } + // create an underlying canvas to be used for special features. + this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this)); + this.baseCanvas.setContext(); + this.target.append(this.title.draw()); + this.title.pack({top:0, left:0}); + + // make room for the legend between the grid and the edge. + // pass a dummy offsets object and a reference to the plot. + var legendElem = this.legend.draw({}, this); + + var gridPadding = {top:0, left:0, bottom:0, right:0}; + + if (this.legend.placement == "outsideGrid") { + // temporarily append the legend to get dimensions + this.target.append(legendElem); + switch (this.legend.location) { + case 'n': + gridPadding.top += this.legend.getHeight(); + break; + case 's': + gridPadding.bottom += this.legend.getHeight(); + break; + case 'ne': + case 'e': + case 'se': + gridPadding.right += this.legend.getWidth(); + break; + case 'nw': + case 'w': + case 'sw': + gridPadding.left += this.legend.getWidth(); + break; + default: // same as 'ne' + gridPadding.right += this.legend.getWidth(); + break; + } + legendElem = legendElem.detach(); + } + + var ax = this.axes; + var name; + // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed. + for (i=0; i<12; i++) { + name = _axisNames[i]; + this.target.append(ax[name].draw(this.baseCanvas._ctx, this)); + ax[name].set(); + } + if (ax.yaxis.show) { + gridPadding.left += ax.yaxis.getWidth(); + } + var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; + var rapad = [0, 0, 0, 0, 0, 0, 0, 0]; + var gpr = 0; + var n; + for (n=0; n<8; n++) { + if (ax[ra[n]].show) { + gpr += ax[ra[n]].getWidth(); + rapad[n] = gpr; + } + } + gridPadding.right += gpr; + if (ax.x2axis.show) { + gridPadding.top += ax.x2axis.getHeight(); + } + if (this.title.show) { + gridPadding.top += this.title.getHeight(); + } + if (ax.xaxis.show) { + gridPadding.bottom += ax.xaxis.getHeight(); + } + + // end of gridPadding adjustments. + + // if user passed in gridDimensions option, check against calculated gridPadding + if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) { + var gdw = parseInt(this.options.gridDimensions.width, 10) || 0; + var gdh = parseInt(this.options.gridDimensions.height, 10) || 0; + var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2; + var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2; + + if (heightAdj >= 0 && widthAdj >= 0) { + gridPadding.top += heightAdj; + gridPadding.bottom += heightAdj; + gridPadding.left += widthAdj; + gridPadding.right += widthAdj; + } + } + var arr = ['top', 'bottom', 'left', 'right']; + for (var n in arr) { + if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) { + this._gridPadding[arr[n]] = gridPadding[arr[n]]; + } + else if (this._gridPadding[arr[n]] == null) { + this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]]; + } + } + + var legendPadding = this._gridPadding; + + if (this.legend.placement === 'outsideGrid') { + legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0}; + if (this.legend.location === 's') { + legendPadding.left = this._gridPadding.left; + legendPadding.right = this._gridPadding.right; + } + } + + ax.xaxis.pack({position:'absolute', bottom:this._gridPadding.bottom - ax.xaxis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right}); + ax.yaxis.pack({position:'absolute', top:0, left:this._gridPadding.left - ax.yaxis.getWidth(), height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); + ax.x2axis.pack({position:'absolute', top:this._gridPadding.top - ax.x2axis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right}); + for (i=8; i>0; i--) { + ax[ra[i-1]].pack({position:'absolute', top:0, right:this._gridPadding.right - rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); + } + var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0; + ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); + + this.target.append(this.grid.createElement(this._gridPadding, this)); + this.grid.draw(); + + var series = this.series; + var seriesLength = series.length; + // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars. + for (i=0, l=seriesLength; i<l; i++) { + // draw series in order of stacking. This affects only + // order in which canvases are added to dom. + j = this.seriesStack[i]; + this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this)); + series[j].shadowCanvas.setContext(); + series[j].shadowCanvas._elem.data('seriesIndex', j); + } + + for (i=0, l=seriesLength; i<l; i++) { + // draw series in order of stacking. This affects only + // order in which canvases are added to dom. + j = this.seriesStack[i]; + this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this)); + series[j].canvas.setContext(); + series[j].canvas._elem.data('seriesIndex', j); + } + // Need to use filled canvas to capture events in IE. + // Also, canvas seems to block selection of other elements in document on FF. + this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this)); + this.eventCanvas.setContext(); + this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)'; + this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height); + + // bind custom event handlers to regular events. + this.bindCustomEvents(); + + // draw legend before series if the series needs to know the legend dimensions. + if (this.legend.preDraw) { + this.eventCanvas._elem.before(legendElem); + this.legend.pack(legendPadding); + if (this.legend._elem) { + this.drawSeries({legendInfo:{location:this.legend.location, placement:this.legend.placement, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}}); + } + else { + this.drawSeries(); + } + } + else { // draw series before legend + this.drawSeries(); + if (seriesLength) { + $(series[seriesLength-1].canvas._elem).after(legendElem); + } + this.legend.pack(legendPadding); + } + + // register event listeners on the overlay canvas + for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) { + // in the handler, this will refer to the eventCanvas dom element. + // make sure there are references back into plot objects. + this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); + } + + // register event listeners on the overlay canvas + for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) { + // in the handler, this will refer to the eventCanvas dom element. + // make sure there are references back into plot objects. + this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]); + } + + var fb = this.fillBetween; + if (fb.fill && fb.series1 !== fb.series2 && fb.series1 < seriesLength && fb.series2 < seriesLength && series[fb.series1]._type === 'line' && series[fb.series2]._type === 'line') { + this.doFillBetweenLines(); + } + + for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) { + $.jqplot.postDrawHooks[i].call(this); + } + + for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) { + this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]); + } + + if (this.target.is(':visible')) { + this._drawCount += 1; + } + + var temps, + tempr, + sel, + _els; + // ughh. ideally would hide all series then show them. + for (i=0, l=seriesLength; i<l; i++) { + temps = series[i]; + tempr = temps.renderer; + sel = '.jqplot-point-label.jqplot-series-'+i; + if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) { + _els = this.target.find(sel); + _els.stop(true, true).hide(); + temps.canvas._elem.stop(true, true).hide(); + temps.shadowCanvas._elem.stop(true, true).hide(); + temps.canvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); + temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); + _els.fadeIn(tempr.animation.speed*0.8); + } + } + _els = null; + + this.target.trigger('jqplotPostDraw', [this]); + } + }; + + jqPlot.prototype.doFillBetweenLines = function () { + var fb = this.fillBetween; + var sid1 = fb.series1; + var sid2 = fb.series2; + // first series should always be lowest index + var id1 = (sid1 < sid2) ? sid1 : sid2; + var id2 = (sid2 > sid1) ? sid2 : sid1; + + var series1 = this.series[id1]; + var series2 = this.series[id2]; + + if (series2.renderer.smooth) { + var tempgd = series2.renderer._smoothedData.slice(0).reverse(); + } + else { + var tempgd = series2.gridData.slice(0).reverse(); + } + + if (series1.renderer.smooth) { + var gd = series1.renderer._smoothedData.concat(tempgd); + } + else { + var gd = series1.gridData.concat(tempgd); + } + + var color = (fb.color !== null) ? fb.color : this.series[sid1].fillColor; + var baseSeries = (fb.baseSeries !== null) ? fb.baseSeries : id1; + + // now apply a fill to the shape on the lower series shadow canvas, + // so it is behind both series. + var sr = this.series[baseSeries].renderer.shapeRenderer; + var opts = {fillStyle: color, fill: true, closePath: true}; + sr.draw(series1.shadowCanvas._ctx, gd, opts); + }; + + this.bindCustomEvents = function() { + this.eventCanvas._elem.bind('click', {plot:this}, this.onClick); + this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); + this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown); + this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove); + this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); + if (this.captureRightClick) { + this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick); + this.eventCanvas._elem.get(0).oncontextmenu = function() { + return false; + }; + } + else { + this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp); + } + }; + + function getEventPosition(ev) { + var plot = ev.data.plot; + var go = plot.eventCanvas._elem.offset(); + var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; + var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null}; + var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; + var ax = plot.axes; + var n, axis; + for (n=11; n>0; n--) { + axis = an[n-1]; + if (ax[axis].show) { + dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); + } + } + + return {offsets:go, gridPos:gridPos, dataPos:dataPos}; + } + + + // function to check if event location is over a area area + function checkIntersection(gridpos, plot) { + var series = plot.series; + var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang; + var d0, d, p, pp, points, bw, hp; + var threshold, t; + for (k=plot.seriesStack.length-1; k>=0; k--) { + i = plot.seriesStack[k]; + s = series[i]; + hp = s._highlightThreshold; + switch (s.renderer.constructor) { + case $.jqplot.BarRenderer: + x = gridpos.x; + y = gridpos.y; + for (j=0; j<s._barPoints.length; j++) { + points = s._barPoints[j]; + p = s.gridData[j]; + if (x>points[0][0] && x<points[2][0] && y>points[2][1] && y<points[0][1]) { + return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; + } + } + break; + case $.jqplot.PyramidRenderer: + x = gridpos.x; + y = gridpos.y; + for (j=0; j<s._barPoints.length; j++) { + points = s._barPoints[j]; + p = s.gridData[j]; + if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) { + return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; + } + } + break; + + case $.jqplot.DonutRenderer: + sa = s.startAngle/180*Math.PI; + x = gridpos.x - s._center[0]; + y = gridpos.y - s._center[1]; + r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + if (x > 0 && -y >= 0) { + theta = 2*Math.PI - Math.atan(-y/x); + } + else if (x > 0 && -y < 0) { + theta = -Math.atan(-y/x); + } + else if (x < 0) { + theta = Math.PI - Math.atan(-y/x); + } + else if (x == 0 && -y > 0) { + theta = 3*Math.PI/2; + } + else if (x == 0 && -y < 0) { + theta = Math.PI/2; + } + else if (x == 0 && y == 0) { + theta = 0; + } + if (sa) { + theta -= sa; + if (theta < 0) { + theta += 2*Math.PI; + } + else if (theta > 2*Math.PI) { + theta -= 2*Math.PI; + } + } + + sm = s.sliceMargin/180*Math.PI; + if (r < s._radius && r > s._innerRadius) { + for (j=0; j<s.gridData.length; j++) { + minang = (j>0) ? s.gridData[j-1][1]+sm : sm; + maxang = s.gridData[j][1]; + if (theta > minang && theta < maxang) { + return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; + } + } + } + break; + + case $.jqplot.PieRenderer: + sa = s.startAngle/180*Math.PI; + x = gridpos.x - s._center[0]; + y = gridpos.y - s._center[1]; + r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + if (x > 0 && -y >= 0) { + theta = 2*Math.PI - Math.atan(-y/x); + } + else if (x > 0 && -y < 0) { + theta = -Math.atan(-y/x); + } + else if (x < 0) { + theta = Math.PI - Math.atan(-y/x); + } + else if (x == 0 && -y > 0) { + theta = 3*Math.PI/2; + } + else if (x == 0 && -y < 0) { + theta = Math.PI/2; + } + else if (x == 0 && y == 0) { + theta = 0; + } + if (sa) { + theta -= sa; + if (theta < 0) { + theta += 2*Math.PI; + } + else if (theta > 2*Math.PI) { + theta -= 2*Math.PI; + } + } + + sm = s.sliceMargin/180*Math.PI; + if (r < s._radius) { + for (j=0; j<s.gridData.length; j++) { + minang = (j>0) ? s.gridData[j-1][1]+sm : sm; + maxang = s.gridData[j][1]; + if (theta > minang && theta < maxang) { + return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; + } + } + } + break; + + case $.jqplot.BubbleRenderer: + x = gridpos.x; + y = gridpos.y; + var ret = null; + + if (s.show) { + for (var j=0; j<s.gridData.length; j++) { + p = s.gridData[j]; + d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); + if (d <= p[2] && (d <= d0 || d0 == null)) { + d0 = d; + ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + if (ret != null) { + return ret; + } + } + break; + + case $.jqplot.FunnelRenderer: + x = gridpos.x; + y = gridpos.y; + var v = s._vertices, + vfirst = v[0], + vlast = v[v.length-1], + lex, + rex, + cv; + + // equations of right and left sides, returns x, y values given height of section (y value and 2 points) + + function findedge (l, p1 , p2) { + var m = (p1[1] - p2[1])/(p1[0] - p2[0]); + var b = p1[1] - m*p1[0]; + var y = l + p1[1]; + + return [(y - b)/m, y]; + } + + // check each section + lex = findedge(y, vfirst[0], vlast[3]); + rex = findedge(y, vfirst[1], vlast[2]); + for (j=0; j<v.length; j++) { + cv = v[j]; + if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) { + return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]}; + } + } + break; + + case $.jqplot.LineRenderer: + x = gridpos.x; + y = gridpos.y; + r = s.renderer; + if (s.show) { + if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) { + // first check if it is in bounding box + var inside = false; + if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { + // now check the crossing number + + var numPoints = s._areaPoints.length; + var ii; + var j = numPoints-1; + + for(var ii=0; ii < numPoints; ii++) { + var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]]; + var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]]; + + if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y) { + if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) { + inside = !inside; + } + } + + j = ii; + } + } + if (inside) { + return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints}; + } + break; + + } + + else { + t = s.markerRenderer.size/2+s.neighborThreshold; + threshold = (t > 0) ? t : 0; + for (var j=0; j<s.gridData.length; j++) { + p = s.gridData[j]; + // neighbor looks different to OHLC chart. + if (r.constructor == $.jqplot.OHLCRenderer) { + if (r.candleStick) { + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + // if an open hi low close chart + else if (!r.hlc){ + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + // a hi low close chart + else { + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + + } + else if (p[0] != null && p[1] != null){ + d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); + if (d <= threshold && (d <= d0 || d0 == null)) { + d0 = d; + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + } + } + } + break; + + default: + x = gridpos.x; + y = gridpos.y; + r = s.renderer; + if (s.show) { + t = s.markerRenderer.size/2+s.neighborThreshold; + threshold = (t > 0) ? t : 0; + for (var j=0; j<s.gridData.length; j++) { + p = s.gridData[j]; + // neighbor looks different to OHLC chart. + if (r.constructor == $.jqplot.OHLCRenderer) { + if (r.candleStick) { + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + // if an open hi low close chart + else if (!r.hlc){ + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + // a hi low close chart + else { + var yp = s._yaxis.series_u2p; + if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + + } + else { + d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); + if (d <= threshold && (d <= d0 || d0 == null)) { + d0 = d; + return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; + } + } + } + } + break; + } + } + + return null; + } + + + + this.onClick = function(ev) { + // Event passed in is normalized and will have data attribute. + // Event passed out is unnormalized. + var positions = getEventPosition(ev); + var p = ev.data.plot; + var neighbor = checkIntersection(positions.gridPos, p); + var evt = $.Event('jqplotClick'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + }; + + this.onDblClick = function(ev) { + // Event passed in is normalized and will have data attribute. + // Event passed out is unnormalized. + var positions = getEventPosition(ev); + var p = ev.data.plot; + var neighbor = checkIntersection(positions.gridPos, p); + var evt = $.Event('jqplotDblClick'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + }; + + this.onMouseDown = function(ev) { + var positions = getEventPosition(ev); + var p = ev.data.plot; + var neighbor = checkIntersection(positions.gridPos, p); + var evt = $.Event('jqplotMouseDown'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + }; + + this.onMouseUp = function(ev) { + var positions = getEventPosition(ev); + var evt = $.Event('jqplotMouseUp'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]); + }; + + this.onRightClick = function(ev) { + var positions = getEventPosition(ev); + var p = ev.data.plot; + var neighbor = checkIntersection(positions.gridPos, p); + if (p.captureRightClick) { + if (ev.which == 3) { + var evt = $.Event('jqplotRightClick'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + } + else { + var evt = $.Event('jqplotMouseUp'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + } + } + }; + + this.onMouseMove = function(ev) { + var positions = getEventPosition(ev); + var p = ev.data.plot; + var neighbor = checkIntersection(positions.gridPos, p); + var evt = $.Event('jqplotMouseMove'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); + }; + + this.onMouseEnter = function(ev) { + var positions = getEventPosition(ev); + var p = ev.data.plot; + var evt = $.Event('jqplotMouseEnter'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + evt.relatedTarget = ev.relatedTarget; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); + }; + + this.onMouseLeave = function(ev) { + var positions = getEventPosition(ev); + var p = ev.data.plot; + var evt = $.Event('jqplotMouseLeave'); + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + evt.relatedTarget = ev.relatedTarget; + $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); + }; + + // method: drawSeries + // Redraws all or just one series on the plot. No axis scaling + // is performed and no other elements on the plot are redrawn. + // options is an options object to pass on to the series renderers. + // It can be an empty object {}. idx is the series index + // to redraw if only one series is to be redrawn. + this.drawSeries = function(options, idx){ + var i, series, ctx; + // if only one argument passed in and it is a number, use it ad idx. + idx = (typeof(options) === "number" && idx == null) ? options : idx; + options = (typeof(options) === "object") ? options : {}; + // draw specified series + if (idx != undefined) { + series = this.series[idx]; + ctx = series.shadowCanvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + series.drawShadow(ctx, options, this); + ctx = series.canvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + series.draw(ctx, options, this); + if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) { + if (idx < this.series.length - 1) { + this.drawSeries(idx+1); + } + } + } + + else { + // if call series drawShadow method first, in case all series shadows + // should be drawn before any series. This will ensure, like for + // stacked bar plots, that shadows don't overlap series. + for (i=0; i<this.series.length; i++) { + // first clear the canvas + series = this.series[i]; + ctx = series.shadowCanvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + series.drawShadow(ctx, options, this); + ctx = series.canvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + series.draw(ctx, options, this); + } + } + options = idx = i = series = ctx = null; + }; + + // method: moveSeriesToFront + // This method requires jQuery 1.4+ + // Moves the specified series canvas in front of all other series canvases. + // This effectively "draws" the specified series on top of all other series, + // although it is performed through DOM manipulation, no redrawing is performed. + // + // Parameters: + // idx - 0 based index of the series to move. This will be the index of the series + // as it was first passed into the jqplot function. + this.moveSeriesToFront = function (idx) { + idx = parseInt(idx, 10); + var stackIndex = $.inArray(idx, this.seriesStack); + // if already in front, return + if (stackIndex == -1) { + return; + } + if (stackIndex == this.seriesStack.length -1) { + this.previousSeriesStack = this.seriesStack.slice(0); + return; + } + var opidx = this.seriesStack[this.seriesStack.length -1]; + var serelem = this.series[idx].canvas._elem.detach(); + var shadelem = this.series[idx].shadowCanvas._elem.detach(); + this.series[opidx].shadowCanvas._elem.after(shadelem); + this.series[opidx].canvas._elem.after(serelem); + this.previousSeriesStack = this.seriesStack.slice(0); + this.seriesStack.splice(stackIndex, 1); + this.seriesStack.push(idx); + }; + + // method: moveSeriesToBack + // This method requires jQuery 1.4+ + // Moves the specified series canvas behind all other series canvases. + // + // Parameters: + // idx - 0 based index of the series to move. This will be the index of the series + // as it was first passed into the jqplot function. + this.moveSeriesToBack = function (idx) { + idx = parseInt(idx, 10); + var stackIndex = $.inArray(idx, this.seriesStack); + // if already in back, return + if (stackIndex == 0 || stackIndex == -1) { + return; + } + var opidx = this.seriesStack[0]; + var serelem = this.series[idx].canvas._elem.detach(); + var shadelem = this.series[idx].shadowCanvas._elem.detach(); + this.series[opidx].shadowCanvas._elem.before(shadelem); + this.series[opidx].canvas._elem.before(serelem); + this.previousSeriesStack = this.seriesStack.slice(0); + this.seriesStack.splice(stackIndex, 1); + this.seriesStack.unshift(idx); + }; + + // method: restorePreviousSeriesOrder + // This method requires jQuery 1.4+ + // Restore the series canvas order to its previous state. + // Useful to put a series back where it belongs after moving + // it to the front. + this.restorePreviousSeriesOrder = function () { + var i, j, serelem, shadelem, temp, move, keep; + // if no change, return. + if (this.seriesStack == this.previousSeriesStack) { + return; + } + for (i=1; i<this.previousSeriesStack.length; i++) { + move = this.previousSeriesStack[i]; + keep = this.previousSeriesStack[i-1]; + serelem = this.series[move].canvas._elem.detach(); + shadelem = this.series[move].shadowCanvas._elem.detach(); + this.series[keep].shadowCanvas._elem.after(shadelem); + this.series[keep].canvas._elem.after(serelem); + } + temp = this.seriesStack.slice(0); + this.seriesStack = this.previousSeriesStack.slice(0); + this.previousSeriesStack = temp; + }; + + // method: restoreOriginalSeriesOrder + // This method requires jQuery 1.4+ + // Restore the series canvas order to its original order + // when the plot was created. + this.restoreOriginalSeriesOrder = function () { + var i, j, arr=[], serelem, shadelem; + for (i=0; i<this.series.length; i++) { + arr.push(i); + } + if (this.seriesStack == arr) { + return; + } + this.previousSeriesStack = this.seriesStack.slice(0); + this.seriesStack = arr; + for (i=1; i<this.seriesStack.length; i++) { + serelem = this.series[i].canvas._elem.detach(); + shadelem = this.series[i].shadowCanvas._elem.detach(); + this.series[i-1].shadowCanvas._elem.after(shadelem); + this.series[i-1].canvas._elem.after(serelem); + } + }; + + this.activateTheme = function (name) { + this.themeEngine.activate(this, name); + }; + } + + + // conpute a highlight color or array of highlight colors from given colors. + $.jqplot.computeHighlightColors = function(colors) { + var ret; + if ($.isArray(colors)) { + ret = []; + for (var i=0; i<colors.length; i++){ + var rgba = $.jqplot.getColorComponents(colors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; + newrgb[j] = parseInt(newrgb[j], 10); + (newrgb[j] > 255) ? 255 : newrgb[j]; + } + // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; + // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; + newrgb[3] = 0.3 + 0.35 * rgba[3]; + ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'); + } + } + else { + var rgba = $.jqplot.getColorComponents(colors); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + // newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + // newrgb[j] = parseInt(newrgb[j], 10); + newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; + newrgb[j] = parseInt(newrgb[j], 10); + (newrgb[j] > 255) ? 255 : newrgb[j]; + } + // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; + // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; + newrgb[3] = 0.3 + 0.35 * rgba[3]; + ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'; + } + return ret; + }; + + $.jqplot.ColorGenerator = function(colors) { + colors = colors || $.jqplot.config.defaultColors; + var idx = 0; + + this.next = function () { + if (idx < colors.length) { + return colors[idx++]; + } + else { + idx = 0; + return colors[idx++]; + } + }; + + this.previous = function () { + if (idx > 0) { + return colors[idx--]; + } + else { + idx = colors.length-1; + return colors[idx]; + } + }; + + // get a color by index without advancing pointer. + this.get = function(i) { + var idx = i - colors.length * Math.floor(i/colors.length); + return colors[idx]; + }; + + this.setColors = function(c) { + colors = c; + }; + + this.reset = function() { + idx = 0; + }; + + this.getIndex = function() { + return idx; + }; + + this.setIndex = function(index) { + idx = index; + }; + }; + + // convert a hex color string to rgb string. + // h - 3 or 6 character hex string, with or without leading # + // a - optional alpha + $.jqplot.hex2rgb = function(h, a) { + h = h.replace('#', ''); + if (h.length == 3) { + h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2); + } + var rgb; + rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16); + if (a) { + rgb += ', '+a; + } + rgb += ')'; + return rgb; + }; + + // convert an rgb color spec to a hex spec. ignore any alpha specification. + $.jqplot.rgb2hex = function(s) { + var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/; + var m = s.match(pat); + var h = '#'; + for (var i=1; i<4; i++) { + var temp; + if (m[i].search(/%/) != -1) { + temp = parseInt(255*m[i]/100, 10).toString(16); + if (temp.length == 1) { + temp = '0'+temp; + } + } + else { + temp = parseInt(m[i], 10).toString(16); + if (temp.length == 1) { + temp = '0'+temp; + } + } + h += temp; + } + return h; + }; + + // given a css color spec, return an rgb css color spec + $.jqplot.normalize2rgb = function(s, a) { + if (s.search(/^ *rgba?\(/) != -1) { + return s; + } + else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) { + return $.jqplot.hex2rgb(s, a); + } + else { + throw new Error('Invalid color spec'); + } + }; + + // extract the r, g, b, a color components out of a css color spec. + $.jqplot.getColorComponents = function(s) { + // check to see if a color keyword. + s = $.jqplot.colorKeywordMap[s] || s; + var rgb = $.jqplot.normalize2rgb(s); + var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/; + var m = rgb.match(pat); + var ret = []; + for (var i=1; i<4; i++) { + if (m[i].search(/%/) != -1) { + ret[i-1] = parseInt(255*m[i]/100, 10); + } + else { + ret[i-1] = parseInt(m[i], 10); + } + } + ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0; + return ret; + }; + + $.jqplot.colorKeywordMap = { + aliceblue: 'rgb(240, 248, 255)', + antiquewhite: 'rgb(250, 235, 215)', + aqua: 'rgb( 0, 255, 255)', + aquamarine: 'rgb(127, 255, 212)', + azure: 'rgb(240, 255, 255)', + beige: 'rgb(245, 245, 220)', + bisque: 'rgb(255, 228, 196)', + black: 'rgb( 0, 0, 0)', + blanchedalmond: 'rgb(255, 235, 205)', + blue: 'rgb( 0, 0, 255)', + blueviolet: 'rgb(138, 43, 226)', + brown: 'rgb(165, 42, 42)', + burlywood: 'rgb(222, 184, 135)', + cadetblue: 'rgb( 95, 158, 160)', + chartreuse: 'rgb(127, 255, 0)', + chocolate: 'rgb(210, 105, 30)', + coral: 'rgb(255, 127, 80)', + cornflowerblue: 'rgb(100, 149, 237)', + cornsilk: 'rgb(255, 248, 220)', + crimson: 'rgb(220, 20, 60)', + cyan: 'rgb( 0, 255, 255)', + darkblue: 'rgb( 0, 0, 139)', + darkcyan: 'rgb( 0, 139, 139)', + darkgoldenrod: 'rgb(184, 134, 11)', + darkgray: 'rgb(169, 169, 169)', + darkgreen: 'rgb( 0, 100, 0)', + darkgrey: 'rgb(169, 169, 169)', + darkkhaki: 'rgb(189, 183, 107)', + darkmagenta: 'rgb(139, 0, 139)', + darkolivegreen: 'rgb( 85, 107, 47)', + darkorange: 'rgb(255, 140, 0)', + darkorchid: 'rgb(153, 50, 204)', + darkred: 'rgb(139, 0, 0)', + darksalmon: 'rgb(233, 150, 122)', + darkseagreen: 'rgb(143, 188, 143)', + darkslateblue: 'rgb( 72, 61, 139)', + darkslategray: 'rgb( 47, 79, 79)', + darkslategrey: 'rgb( 47, 79, 79)', + darkturquoise: 'rgb( 0, 206, 209)', + darkviolet: 'rgb(148, 0, 211)', + deeppink: 'rgb(255, 20, 147)', + deepskyblue: 'rgb( 0, 191, 255)', + dimgray: 'rgb(105, 105, 105)', + dimgrey: 'rgb(105, 105, 105)', + dodgerblue: 'rgb( 30, 144, 255)', + firebrick: 'rgb(178, 34, 34)', + floralwhite: 'rgb(255, 250, 240)', + forestgreen: 'rgb( 34, 139, 34)', + fuchsia: 'rgb(255, 0, 255)', + gainsboro: 'rgb(220, 220, 220)', + ghostwhite: 'rgb(248, 248, 255)', + gold: 'rgb(255, 215, 0)', + goldenrod: 'rgb(218, 165, 32)', + gray: 'rgb(128, 128, 128)', + grey: 'rgb(128, 128, 128)', + green: 'rgb( 0, 128, 0)', + greenyellow: 'rgb(173, 255, 47)', + honeydew: 'rgb(240, 255, 240)', + hotpink: 'rgb(255, 105, 180)', + indianred: 'rgb(205, 92, 92)', + indigo: 'rgb( 75, 0, 130)', + ivory: 'rgb(255, 255, 240)', + khaki: 'rgb(240, 230, 140)', + lavender: 'rgb(230, 230, 250)', + lavenderblush: 'rgb(255, 240, 245)', + lawngreen: 'rgb(124, 252, 0)', + lemonchiffon: 'rgb(255, 250, 205)', + lightblue: 'rgb(173, 216, 230)', + lightcoral: 'rgb(240, 128, 128)', + lightcyan: 'rgb(224, 255, 255)', + lightgoldenrodyellow: 'rgb(250, 250, 210)', + lightgray: 'rgb(211, 211, 211)', + lightgreen: 'rgb(144, 238, 144)', + lightgrey: 'rgb(211, 211, 211)', + lightpink: 'rgb(255, 182, 193)', + lightsalmon: 'rgb(255, 160, 122)', + lightseagreen: 'rgb( 32, 178, 170)', + lightskyblue: 'rgb(135, 206, 250)', + lightslategray: 'rgb(119, 136, 153)', + lightslategrey: 'rgb(119, 136, 153)', + lightsteelblue: 'rgb(176, 196, 222)', + lightyellow: 'rgb(255, 255, 224)', + lime: 'rgb( 0, 255, 0)', + limegreen: 'rgb( 50, 205, 50)', + linen: 'rgb(250, 240, 230)', + magenta: 'rgb(255, 0, 255)', + maroon: 'rgb(128, 0, 0)', + mediumaquamarine: 'rgb(102, 205, 170)', + mediumblue: 'rgb( 0, 0, 205)', + mediumorchid: 'rgb(186, 85, 211)', + mediumpurple: 'rgb(147, 112, 219)', + mediumseagreen: 'rgb( 60, 179, 113)', + mediumslateblue: 'rgb(123, 104, 238)', + mediumspringgreen: 'rgb( 0, 250, 154)', + mediumturquoise: 'rgb( 72, 209, 204)', + mediumvioletred: 'rgb(199, 21, 133)', + midnightblue: 'rgb( 25, 25, 112)', + mintcream: 'rgb(245, 255, 250)', + mistyrose: 'rgb(255, 228, 225)', + moccasin: 'rgb(255, 228, 181)', + navajowhite: 'rgb(255, 222, 173)', + navy: 'rgb( 0, 0, 128)', + oldlace: 'rgb(253, 245, 230)', + olive: 'rgb(128, 128, 0)', + olivedrab: 'rgb(107, 142, 35)', + orange: 'rgb(255, 165, 0)', + orangered: 'rgb(255, 69, 0)', + orchid: 'rgb(218, 112, 214)', + palegoldenrod: 'rgb(238, 232, 170)', + palegreen: 'rgb(152, 251, 152)', + paleturquoise: 'rgb(175, 238, 238)', + palevioletred: 'rgb(219, 112, 147)', + papayawhip: 'rgb(255, 239, 213)', + peachpuff: 'rgb(255, 218, 185)', + peru: 'rgb(205, 133, 63)', + pink: 'rgb(255, 192, 203)', + plum: 'rgb(221, 160, 221)', + powderblue: 'rgb(176, 224, 230)', + purple: 'rgb(128, 0, 128)', + red: 'rgb(255, 0, 0)', + rosybrown: 'rgb(188, 143, 143)', + royalblue: 'rgb( 65, 105, 225)', + saddlebrown: 'rgb(139, 69, 19)', + salmon: 'rgb(250, 128, 114)', + sandybrown: 'rgb(244, 164, 96)', + seagreen: 'rgb( 46, 139, 87)', + seashell: 'rgb(255, 245, 238)', + sienna: 'rgb(160, 82, 45)', + silver: 'rgb(192, 192, 192)', + skyblue: 'rgb(135, 206, 235)', + slateblue: 'rgb(106, 90, 205)', + slategray: 'rgb(112, 128, 144)', + slategrey: 'rgb(112, 128, 144)', + snow: 'rgb(255, 250, 250)', + springgreen: 'rgb( 0, 255, 127)', + steelblue: 'rgb( 70, 130, 180)', + tan: 'rgb(210, 180, 140)', + teal: 'rgb( 0, 128, 128)', + thistle: 'rgb(216, 191, 216)', + tomato: 'rgb(255, 99, 71)', + turquoise: 'rgb( 64, 224, 208)', + violet: 'rgb(238, 130, 238)', + wheat: 'rgb(245, 222, 179)', + white: 'rgb(255, 255, 255)', + whitesmoke: 'rgb(245, 245, 245)', + yellow: 'rgb(255, 255, 0)', + yellowgreen: 'rgb(154, 205, 50)' + }; + + + + + // class: $.jqplot.AxisLabelRenderer + // Renderer to place labels on the axes. + $.jqplot.AxisLabelRenderer = function(options) { + // Group: Properties + $.jqplot.ElemContainer.call(this); + // name of the axis associated with this tick + this.axis; + // prop: show + // whether or not to show the tick (mark and label). + this.show = true; + // prop: label + // The text or html for the label. + this.label = ''; + this.fontFamily = null; + this.fontSize = null; + this.textColor = null; + this._elem; + // prop: escapeHTML + // true to escape HTML entities in the label. + this.escapeHTML = false; + + $.extend(true, this, options); + }; + + $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer(); + $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer; + + $.jqplot.AxisLabelRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) { + // Memory Leaks patch + if (this._elem) { + this._elem.emptyForce(); + this._elem = null; + } + + this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>'); + + if (Number(this.label)) { + this._elem.css('white-space', 'nowrap'); + } + + if (!this.escapeHTML) { + this._elem.html(this.label); + } + else { + this._elem.text(this.label); + } + if (this.fontFamily) { + this._elem.css('font-family', this.fontFamily); + } + if (this.fontSize) { + this._elem.css('font-size', this.fontSize); + } + if (this.textColor) { + this._elem.css('color', this.textColor); + } + + return this._elem; + }; + + $.jqplot.AxisLabelRenderer.prototype.pack = function() { + }; + + // class: $.jqplot.AxisTickRenderer + // A "tick" object showing the value of a tick/gridline on the plot. + $.jqplot.AxisTickRenderer = function(options) { + // Group: Properties + $.jqplot.ElemContainer.call(this); + // prop: mark + // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. + this.mark = 'outside'; + // name of the axis associated with this tick + this.axis; + // prop: showMark + // whether or not to show the mark on the axis. + this.showMark = true; + // prop: showGridline + // whether or not to draw the gridline on the grid at this tick. + this.showGridline = true; + // prop: isMinorTick + // if this is a minor tick. + this.isMinorTick = false; + // prop: size + // Length of the tick beyond the grid in pixels. + // DEPRECATED: This has been superceeded by markSize + this.size = 4; + // prop: markSize + // Length of the tick marks in pixels. For 'cross' style, length + // will be stoked above and below axis, so total length will be twice this. + this.markSize = 6; + // prop: show + // whether or not to show the tick (mark and label). + // Setting this to false requires more testing. It is recommended + // to set showLabel and showMark to false instead. + this.show = true; + // prop: showLabel + // whether or not to show the label. + this.showLabel = true; + this.label = null; + this.value = null; + this._styles = {}; + // prop: formatter + // A class of a formatter for the tick text. sprintf by default. + this.formatter = $.jqplot.DefaultTickFormatter; + // prop: prefix + // String to prepend to the tick label. + // Prefix is prepended to the formatted tick label. + this.prefix = ''; + // prop: suffix + // String to append to the tick label. + // Suffix is appended to the formatted tick label. + this.suffix = ''; + // prop: formatString + // string passed to the formatter. + this.formatString = ''; + // prop: fontFamily + // css spec for the font-family css attribute. + this.fontFamily; + // prop: fontSize + // css spec for the font-size css attribute. + this.fontSize; + // prop: textColor + // css spec for the color attribute. + this.textColor; + // prop: escapeHTML + // true to escape HTML entities in the label. + this.escapeHTML = false; + this._elem; + this._breakTick = false; + + $.extend(true, this, options); + }; + + $.jqplot.AxisTickRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer(); + $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer; + + $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { + this.value = value; + this.axis = axisName; + if (isMinor) { + this.isMinorTick = true; + } + return this; + }; + + $.jqplot.AxisTickRenderer.prototype.draw = function() { + if (this.label === null) { + this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix; + } + var style = {position: 'absolute'}; + if (Number(this.label)) { + style['whitSpace'] = 'nowrap'; + } + + // Memory Leaks patch + if (this._elem) { + this._elem.emptyForce(); + this._elem = null; + } + + this._elem = $(document.createElement('div')); + this._elem.addClass("jqplot-"+this.axis+"-tick"); + + if (!this.escapeHTML) { + this._elem.html(this.label); + } + else { + this._elem.text(this.label); + } + + this._elem.css(style); + + for (var s in this._styles) { + this._elem.css(s, this._styles[s]); + } + if (this.fontFamily) { + this._elem.css('font-family', this.fontFamily); + } + if (this.fontSize) { + this._elem.css('font-size', this.fontSize); + } + if (this.textColor) { + this._elem.css('color', this.textColor); + } + if (this._breakTick) { + this._elem.addClass('jqplot-breakTick'); + } + + return this._elem; + }; + + $.jqplot.DefaultTickFormatter = function (format, val) { + if (typeof val == 'number') { + if (!format) { + format = $.jqplot.config.defaultTickFormatString; + } + return $.jqplot.sprintf(format, val); + } + else { + return String(val); + } + }; + + $.jqplot.PercentTickFormatter = function (format, val) { + if (typeof val == 'number') { + val = 100 * val; + if (!format) { + format = $.jqplot.config.defaultTickFormatString; + } + return $.jqplot.sprintf(format, val); + } + else { + return String(val); + } + }; + + $.jqplot.AxisTickRenderer.prototype.pack = function() { + }; + + // Class: $.jqplot.CanvasGridRenderer + // The default jqPlot grid renderer, creating a grid on a canvas element. + // The renderer has no additional options beyond the <Grid> class. + $.jqplot.CanvasGridRenderer = function(){ + this.shadowRenderer = new $.jqplot.ShadowRenderer(); + }; + + // called with context of Grid object + $.jqplot.CanvasGridRenderer.prototype.init = function(options) { + this._ctx; + $.extend(true, this, options); + // set the shadow renderer options + var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor}; + this.renderer.shadowRenderer.init(sopts); + }; + + // called with context of Grid. + $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) { + var elem; + // Memory Leaks patch + if (this._elem) { + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + elem = this._elem.get(0); + window.G_vmlCanvasManager.uninitElement(elem); + elem = null; + } + + this._elem.emptyForce(); + this._elem = null; + } + + elem = plot.canvasManager.getCanvas(); + + var w = this._plotDimensions.width; + var h = this._plotDimensions.height; + elem.width = w; + elem.height = h; + this._elem = $(elem); + this._elem.addClass('jqplot-grid-canvas'); + this._elem.css({ position: 'absolute', left: 0, top: 0 }); + + elem = plot.canvasManager.initCanvas(elem); + + this._top = this._offsets.top; + this._bottom = h - this._offsets.bottom; + this._left = this._offsets.left; + this._right = w - this._offsets.right; + this._width = this._right - this._left; + this._height = this._bottom - this._top; + // avoid memory leak + elem = null; + return this._elem; + }; + + $.jqplot.CanvasGridRenderer.prototype.draw = function() { + this._ctx = this._elem.get(0).getContext("2d"); + var ctx = this._ctx; + var axes = this._axes; + // Add the grid onto the grid canvas. This is the bottom most layer. + ctx.save(); + ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); + ctx.fillStyle = this.backgroundColor || this.background; + ctx.fillRect(this._left, this._top, this._width, this._height); + + ctx.save(); + ctx.lineJoin = 'miter'; + ctx.lineCap = 'butt'; + ctx.lineWidth = this.gridLineWidth; + ctx.strokeStyle = this.gridLineColor; + var b, e, s, m; + var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis']; + for (var i=4; i>0; i--) { + var name = ax[i-1]; + var axis = axes[name]; + var ticks = axis._ticks; + var numticks = ticks.length; + if (axis.show) { + if (axis.drawBaseline) { + var bopts = {}; + if (axis.baselineWidth !== null) { + bopts.lineWidth = axis.baselineWidth; + } + if (axis.baselineColor !== null) { + bopts.strokeStyle = axis.baselineColor; + } + switch (name) { + case 'xaxis': + drawLine (this._left, this._bottom, this._right, this._bottom, bopts); + break; + case 'yaxis': + drawLine (this._left, this._bottom, this._left, this._top, bopts); + break; + case 'x2axis': + drawLine (this._left, this._bottom, this._right, this._bottom, bopts); + break; + case 'y2axis': + drawLine (this._right, this._bottom, this._right, this._top, bopts); + break; + } + } + for (var j=numticks; j>0; j--) { + var t = ticks[j-1]; + if (t.show) { + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (name) { + case 'xaxis': + // draw the grid line if we should + if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { + drawLine(pos, this._top, pos, this._bottom); + } + // draw the mark + if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._bottom; + e = this._bottom+s; + break; + case 'inside': + b = this._bottom-s; + e = this._bottom; + break; + case 'cross': + b = this._bottom-s; + e = this._bottom+s; + break; + default: + b = this._bottom; + e = this._bottom+s; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); + } + // draw the line + drawLine(pos, b, pos, e); + } + break; + case 'yaxis': + // draw the grid line + if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { + drawLine(this._right, pos, this._left, pos); + } + // draw the mark + if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._left-s; + e = this._left; + break; + case 'inside': + b = this._left; + e = this._left+s; + break; + case 'cross': + b = this._left-s; + e = this._left+s; + break; + default: + b = this._left-s; + e = this._left; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + break; + case 'x2axis': + // draw the grid line + if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { + drawLine(pos, this._bottom, pos, this._top); + } + // draw the mark + if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._top-s; + e = this._top; + break; + case 'inside': + b = this._top; + e = this._top+s; + break; + case 'cross': + b = this._top-s; + e = this._top+s; + break; + default: + b = this._top-s; + e = this._top; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); + } + drawLine(pos, b, pos, e); + } + break; + case 'y2axis': + // draw the grid line + if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { + drawLine(this._left, pos, this._right, pos); + } + // draw the mark + if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._right; + e = this._right+s; + break; + case 'inside': + b = this._right-s; + e = this._right; + break; + case 'cross': + b = this._right-s; + e = this._right+s; + break; + default: + b = this._right; + e = this._right+s; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + break; + default: + break; + } + } + } + t = null; + } + axis = null; + ticks = null; + } + // Now draw grid lines for additional y axes + ////// + // TO DO: handle yMidAxis + ////// + ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; + for (var i=7; i>0; i--) { + var axis = axes[ax[i-1]]; + var ticks = axis._ticks; + if (axis.show) { + var tn = ticks[axis.numberTicks-1]; + var t0 = ticks[0]; + var left = axis.getLeft(); + var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]]; + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false}); + } + // draw the line + drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth}); + // draw the tick marks + for (var j=ticks.length; j>0; j--) { + var t = ticks[j-1]; + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + if (t.showMark && t.mark) { + switch (m) { + case 'outside': + b = left; + e = left+s; + break; + case 'inside': + b = left-s; + e = left; + break; + case 'cross': + b = left-s; + e = left+s; + break; + default: + b = left; + e = left+s; + break; + } + points = [[b,pos], [e,pos]]; + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + // draw the line + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + t = null; + } + t0 = null; + } + axis = null; + ticks = null; + } + + ctx.restore(); + + function drawLine(bx, by, ex, ey, opts) { + ctx.save(); + opts = opts || {}; + if (opts.lineWidth == null || opts.lineWidth != 0){ + $.extend(true, ctx, opts); + ctx.beginPath(); + ctx.moveTo(bx, by); + ctx.lineTo(ex, ey); + ctx.stroke(); + ctx.restore(); + } + } + + if (this.shadow) { + var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; + this.renderer.shadowRenderer.draw(ctx, points); + } + // Now draw border around grid. Use axis border definitions. start at + // upper left and go clockwise. + if (this.borderWidth != 0 && this.drawBorder) { + drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); + drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); + drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); + drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); + } + // ctx.lineWidth = this.borderWidth; + // ctx.strokeStyle = this.borderColor; + // ctx.strokeRect(this._left, this._top, this._width, this._height); + + ctx.restore(); + ctx = null; + axes = null; + }; + + // Class: $.jqplot.DivTitleRenderer + // The default title renderer for jqPlot. This class has no options beyond the <Title> class. + $.jqplot.DivTitleRenderer = function() { + }; + + $.jqplot.DivTitleRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + $.jqplot.DivTitleRenderer.prototype.draw = function() { + // Memory Leaks patch + if (this._elem) { + this._elem.emptyForce(); + this._elem = null; + } + + var r = this.renderer; + var elem = document.createElement('div'); + this._elem = $(elem); + this._elem.addClass('jqplot-title'); + + if (!this.text) { + this.show = false; + this._elem.height(0); + this._elem.width(0); + } + else if (this.text) { + var color; + if (this.color) { + color = this.color; + } + else if (this.textColor) { + color = this.textColor; + } + + // don't trust that a stylesheet is present, set the position. + var styles = {position:'absolute', top:'0px', left:'0px'}; + + if (this._plotWidth) { + styles['width'] = this._plotWidth+'px'; + } + if (this.fontSize) { + styles['fontSize'] = this.fontSize; + } + if (typeof this.textAlign === 'string') { + styles['textAlign'] = this.textAlign; + } + else { + styles['textAlign'] = 'center'; + } + if (color) { + styles['color'] = color; + } + if (this.paddingBottom) { + styles['paddingBottom'] = this.paddingBottom; + } + if (this.fontFamily) { + styles['fontFamily'] = this.fontFamily; + } + + this._elem.css(styles); + if (this.escapeHtml) { + this._elem.text(this.text); + } + else { + this._elem.html(this.text); + } + + + // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : ''; + // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;'; + // styletext += (color) ? 'color:'+color+';' : ''; + // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : ''; + // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>'); + // if (this.fontFamily) { + // this._elem.css('font-family', this.fontFamily); + // } + } + + elem = null; + + return this._elem; + }; + + $.jqplot.DivTitleRenderer.prototype.pack = function() { + // nothing to do here + }; + + + var dotlen = 0.1; + + $.jqplot.LinePattern = function (ctx, pattern) { + + var defaultLinePatterns = { + dotted: [ dotlen, $.jqplot.config.dotGapLength ], + dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ], + solid: null + }; + + if (typeof pattern === 'string') { + if (pattern[0] === '.' || pattern[0] === '-') { + var s = pattern; + pattern = []; + for (var i=0, imax=s.length; i<imax; i++) { + if (s[i] === '.') { + pattern.push( dotlen ); + } + else if (s[i] === '-') { + pattern.push( $.jqplot.config.dashLength ); + } + else { + continue; + } + pattern.push( $.jqplot.config.gapLength ); + } + } + else { + pattern = defaultLinePatterns[pattern]; + } + } + + if (!(pattern && pattern.length)) { + return ctx; + } + + var patternIndex = 0; + var patternDistance = pattern[0]; + var px = 0; + var py = 0; + var pathx0 = 0; + var pathy0 = 0; + + var moveTo = function (x, y) { + ctx.moveTo( x, y ); + px = x; + py = y; + pathx0 = x; + pathy0 = y; + }; + + var lineTo = function (x, y) { + var scale = ctx.lineWidth; + var dx = x - px; + var dy = y - py; + var dist = Math.sqrt(dx*dx+dy*dy); + if ((dist > 0) && (scale > 0)) { + dx /= dist; + dy /= dist; + while (true) { + var dp = scale * patternDistance; + if (dp < dist) { + px += dp * dx; + py += dp * dy; + if ((patternIndex & 1) == 0) { + ctx.lineTo( px, py ); + } + else { + ctx.moveTo( px, py ); + } + dist -= dp; + patternIndex++; + if (patternIndex >= pattern.length) { + patternIndex = 0; + } + patternDistance = pattern[patternIndex]; + } + else { + px = x; + py = y; + if ((patternIndex & 1) == 0) { + ctx.lineTo( px, py ); + } + else { + ctx.moveTo( px, py ); + } + patternDistance -= dist / scale; + break; + } + } + } + }; + + var beginPath = function () { + ctx.beginPath(); + }; + + var closePath = function () { + lineTo( pathx0, pathy0 ); + }; + + return { + moveTo: moveTo, + lineTo: lineTo, + beginPath: beginPath, + closePath: closePath + }; + }; + + // Class: $.jqplot.LineRenderer + // The default line renderer for jqPlot, this class has no options beyond the <Series> class. + // Draws series as a line. + $.jqplot.LineRenderer = function(){ + this.shapeRenderer = new $.jqplot.ShapeRenderer(); + this.shadowRenderer = new $.jqplot.ShadowRenderer(); + }; + + // called with scope of series. + $.jqplot.LineRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + options = options || {}; + this._type='line'; + this.renderer.animation = { + show: false, + direction: 'left', + speed: 2500, + _supported: true + }; + // prop: smooth + // True to draw a smoothed (interpolated) line through the data points + // with automatically computed number of smoothing points. + // Set to an integer number > 2 to specify number of smoothing points + // to use between each data point. + this.renderer.smooth = false; // true or a number > 2 for smoothing. + this.renderer.tension = null; // null to auto compute or a number typically > 6. Fewer points requires higher tension. + // prop: constrainSmoothing + // True to use a more accurate smoothing algorithm that will + // not overshoot any data points. False to allow overshoot but + // produce a smoother looking line. + this.renderer.constrainSmoothing = true; + // this is smoothed data in grid coordinates, like gridData + this.renderer._smoothedData = []; + // this is smoothed data in plot units (plot coordinates), like plotData. + this.renderer._smoothedPlotData = []; + this.renderer._hiBandGridData = []; + this.renderer._lowBandGridData = []; + this.renderer._hiBandSmoothedData = []; + this.renderer._lowBandSmoothedData = []; + + // prop: bandData + // Data used to draw error bands or confidence intervals above/below a line. + // + // bandData can be input in 3 forms. jqPlot will figure out which is the + // low band line and which is the high band line for all forms: + // + // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where + // [yl1, yl2, ...] are y values of the lower line and + // [yu1, yu2, ...] are y values of the upper line. + // In this case there must be the same number of y data points as data points + // in the series and the bands will inherit the x values of the series. + // + // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]] + // where [xl1, yl1] are x,y data points for the lower line and + // [xh1, yh1] are x,y data points for the high line. + // x values do not have to correspond to the x values of the series and can + // be of any arbitrary length. + // + // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where + // there must be 3 or more arrays and there must be the same number of arrays + // as there are data points in the series. In this case, + // [yl1, yu1] specifies the lower and upper y values for the 1st + // data point and so on. The bands will inherit the x + // values from the series. + this.renderer.bandData = []; + + // Group: bands + // Banding around line, e.g error bands or confidence intervals. + this.renderer.bands = { + // prop: show + // true to show the bands. If bandData or interval is + // supplied, show will be set to true by default. + show: false, + hiData: [], + lowData: [], + // prop: color + // color of lines at top and bottom of bands [default: series color]. + color: this.color, + // prop: showLines + // True to show lines at top and bottom of bands [default: false]. + showLines: false, + // prop: fill + // True to fill area between bands [default: true]. + fill: true, + // prop: fillColor + // css color spec for filled area. [default: series color]. + fillColor: null, + _min: null, + _max: null, + // prop: interval + // User specified interval above and below line for bands [default: '3%'']. + // Can be a value like 3 or a string like '3%' + // or an upper/lower array like [1, -2] or ['2%', '-1.5%'] + interval: '3%' + }; + + + var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor}; + + delete (options.highlightMouseOver); + delete (options.highlightMouseDown); + delete (options.highlightColor); + + $.extend(true, this.renderer, options); + + this.renderer.options = options; + + // if we are given some band data, and bands aren't explicity set to false in options, turn them on. + if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) { + this.renderer.bands.show = true; + } + + // if we are given an interval, and bands aren't explicity set to false in options, turn them on. + else if (options.bands && options.bands.show == null && options.bands.interval != null) { + this.renderer.bands.show = true; + } + + // if plot is filled, turn off bands. + if (this.fill) { + this.renderer.bands.show = false; + } + + if (this.renderer.bands.show) { + this.renderer.initBands.call(this, this.renderer.options, plot); + } + + + // smoothing is not compatible with stacked lines, disable + if (this._stack) { + this.renderer.smooth = false; + } + + // set the shape renderer options + var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; + this.renderer.shapeRenderer.init(opts); + + var shadow_offset = options.shadowOffset; + // set the shadow renderer options + if (shadow_offset == null) { + // scale the shadowOffset to the width of the line. + if (this.lineWidth > 2.5) { + shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); + // var shadow_offset = this.shadowOffset; + } + // for skinny lines, don't make such a big shadow. + else { + shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; + } + } + + var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; + this.renderer.shadowRenderer.init(sopts); + this._areaPoints = []; + this._boundingBox = [[],[]]; + + if (!this.isTrendline && this.fill || this.renderer.bands.show) { + // Group: Properties + // + // prop: highlightMouseOver + // True to highlight area on a filled plot when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over an area on a filled plot. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColor + // color to use when highlighting an area on a filled plot. + this.highlightColor = null; + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) { + lopts.highlightMouseOver = false; + } + + $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor}); + + if (!this.highlightColor) { + var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor; + this.highlightColor = $.jqplot.computeHighlightColors(fc); + } + // turn off (disable) the highlighter plugin + if (this.highlighter) { + this.highlighter.show = false; + } + } + + if (!this.isTrendline && plot) { + plot.plugins.lineRenderer = {}; + plot.postInitHooks.addOnce(postInit); + plot.postDrawHooks.addOnce(postPlotDraw); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + } + + }; + + $.jqplot.LineRenderer.prototype.initBands = function(options, plot) { + // use bandData if no data specified in bands option + //var bd = this.renderer.bandData; + var bd = options.bandData || []; + var bands = this.renderer.bands; + bands.hiData = []; + bands.lowData = []; + var data = this.data; + bands._max = null; + bands._min = null; + // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values. + if (bd.length == 2) { + // Do we have an array of x,y values? + // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]] + if ($.isArray(bd[0][0])) { + // since an arbitrary array of points, spin through all of them to determine max and min lines. + + var p; + var bdminidx = 0, bdmaxidx = 0; + for (var i = 0, l = bd[0].length; i<l; i++) { + p = bd[0][i]; + if ((p[1] != null && p[1] > bands._max) || bands._max == null) { + bands._max = p[1]; + } + if ((p[1] != null && p[1] < bands._min) || bands._min == null) { + bands._min = p[1]; + } + } + for (var i = 0, l = bd[1].length; i<l; i++) { + p = bd[1][i]; + if ((p[1] != null && p[1] > bands._max) || bands._max == null) { + bands._max = p[1]; + bdmaxidx = 1; + } + if ((p[1] != null && p[1] < bands._min) || bands._min == null) { + bands._min = p[1]; + bdminidx = 1; + } + } + + if (bdmaxidx === bdminidx) { + bands.show = false; + } + + bands.hiData = bd[bdmaxidx]; + bands.lowData = bd[bdminidx]; + } + // else data is arrays of y values + // like [[1,4,3], [3,6,5]] + // must have same number of band data points as points in series + else if (bd[0].length === data.length && bd[1].length === data.length) { + var hi = (bd[0][0] > bd[1][0]) ? 0 : 1; + var low = (hi) ? 0 : 1; + for (var i=0, l=data.length; i < l; i++) { + bands.hiData.push([data[i][0], bd[hi][i]]); + bands.lowData.push([data[i][0], bd[low][i]]); + } + } + + // we don't have proper data array, don't show bands. + else { + bands.show = false; + } + } + + // if more than 2 arrays, have arrays of [ylow, yhi] values. + // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]] + // this is assumed to be of the latter form. + else if (bd.length > 2 && !$.isArray(bd[0][0])) { + var hi = (bd[0][0] > bd[0][1]) ? 0 : 1; + var low = (hi) ? 0 : 1; + for (var i=0, l=bd.length; i<l; i++) { + bands.hiData.push([data[i][0], bd[i][hi]]); + bands.lowData.push([data[i][0], bd[i][low]]); + } + } + + // don't have proper data, auto calculate + else { + var intrv = bands.interval; + var a = null; + var b = null; + var afunc = null; + var bfunc = null; + + if ($.isArray(intrv)) { + a = intrv[0]; + b = intrv[1]; + } + else { + a = intrv; + } + + if (isNaN(a)) { + // we have a string + if (a.charAt(a.length - 1) === '%') { + afunc = 'multiply'; + a = parseFloat(a)/100 + 1; + } + } + + else { + a = parseFloat(a); + afunc = 'add'; + } + + if (b !== null && isNaN(b)) { + // we have a string + if (b.charAt(b.length - 1) === '%') { + bfunc = 'multiply'; + b = parseFloat(b)/100 + 1; + } + } + + else if (b !== null) { + b = parseFloat(b); + bfunc = 'add'; + } + + if (a !== null) { + if (b === null) { + b = -a; + bfunc = afunc; + if (bfunc === 'multiply') { + b += 2; + } + } + + // make sure a always applies to hi band. + if (a < b) { + var temp = a; + a = b; + b = temp; + temp = afunc; + afunc = bfunc; + bfunc = temp; + } + + for (var i=0, l = data.length; i < l; i++) { + switch (afunc) { + case 'add': + bands.hiData.push([data[i][0], data[i][1] + a]); + break; + case 'multiply': + bands.hiData.push([data[i][0], data[i][1] * a]); + break; + } + switch (bfunc) { + case 'add': + bands.lowData.push([data[i][0], data[i][1] + b]); + break; + case 'multiply': + bands.lowData.push([data[i][0], data[i][1] * b]); + break; + } + } + } + + else { + bands.show = false; + } + } + + var hd = bands.hiData; + var ld = bands.lowData; + for (var i = 0, l = hd.length; i<l; i++) { + if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) { + bands._max = hd[i][1]; + } + } + for (var i = 0, l = ld.length; i<l; i++) { + if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) { + bands._min = ld[i][1]; + } + } + + // one last check for proper data + // these don't apply any more since allowing arbitrary x,y values + // if (bands.hiData.length != bands.lowData.length) { + // bands.show = false; + // } + + // if (bands.hiData.length != this.data.length) { + // bands.show = false; + // } + + if (bands.fillColor === null) { + var c = $.jqplot.getColorComponents(bands.color); + // now adjust alpha to differentiate fill + c[3] = c[3] * 0.5; + bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')'; + } + }; + + function getSteps (d, f) { + return (3.4182054+f) * Math.pow(d, -0.3534992); + } + + function computeSteps (d1, d2) { + var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2)); + return 5.7648 * Math.log(s) + 7.4456; + } + + function tanh (x) { + var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1); + return a; + } + + ////////// + // computeConstrainedSmoothedData + // An implementation of the constrained cubic spline interpolation + // method as presented in: + // + // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications + // http://www.korf.co.uk/spline.pdf + // + // The implementation below borrows heavily from the sample Visual Basic + // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls + // + ///////// + + // called with scope of series + function computeConstrainedSmoothedData (gd) { + var smooth = this.renderer.smooth; + var dim = this.canvas.getWidth(); + var xp = this._xaxis.series_p2u; + var yp = this._yaxis.series_p2u; + var steps =null; + var _steps = null; + var dist = gd.length/dim; + var _smoothedData = []; + var _smoothedPlotData = []; + + if (!isNaN(parseFloat(smooth))) { + steps = parseFloat(smooth); + } + else { + steps = getSteps(dist, 0.5); + } + + var yy = []; + var xx = []; + + for (var i=0, l = gd.length; i<l; i++) { + yy.push(gd[i][1]); + xx.push(gd[i][0]); + } + + function dxx(x1, x0) { + if (x1 - x0 == 0) { + return Math.pow(10,10); + } + else { + return x1 - x0; + } + } + + var A, B, C, D; + // loop through each line segment. Have # points - 1 line segments. Nmber segments starting at 1. + var nmax = gd.length - 1; + for (var num = 1, gdl = gd.length; num<gdl; num++) { + var gxx = []; + var ggxx = []; + // point at each end of segment. + for (var j = 0; j < 2; j++) { + var i = num - 1 + j; // point number, 0 to # points. + + if (i == 0 || i == nmax) { + gxx[j] = Math.pow(10, 10); + } + else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) { + gxx[j] = 0; + } + else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) { + gxx[j] = 0; + } + else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) { + gxx[j] = 0; + } + + else { + gxx[j] = 2 / (dxx(xx[i + 1], xx[i]) / (yy[i + 1] - yy[i]) + dxx(xx[i], xx[i - 1]) / (yy[i] - yy[i - 1])); + } + } + + // Reset first derivative (slope) at first and last point + if (num == 1) { + // First point has 0 2nd derivative + gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2; + } + else if (num == nmax) { + // Last point has 0 2nd derivative + gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2; + } + + // Calc second derivative at points + ggxx[0] = -2 * (gxx[1] + 2 * gxx[0]) / dxx(xx[num], xx[num - 1]) + 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); + ggxx[1] = 2 * (2 * gxx[1] + gxx[0]) / dxx(xx[num], xx[num - 1]) - 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); + + // Calc constants for cubic interpolation + D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]); + C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]); + B = (yy[num] - yy[num - 1] - C * (Math.pow(xx[num], 2) - Math.pow(xx[num - 1], 2)) - D * (Math.pow(xx[num], 3) - Math.pow(xx[num - 1], 3))) / dxx(xx[num], xx[num - 1]); + A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3); + + var increment = (xx[num] - xx[num - 1]) / steps; + var temp, tempx; + + for (var j = 0, l = steps; j < l; j++) { + temp = []; + tempx = xx[num - 1] + j * increment; + temp.push(tempx); + temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3)); + _smoothedData.push(temp); + _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]); + } + } + + _smoothedData.push(gd[i]); + _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]); + + return [_smoothedData, _smoothedPlotData]; + } + + /////// + // computeHermiteSmoothedData + // A hermite spline smoothing of the plot data. + // This implementation is derived from the one posted + // by krypin on the jqplot-users mailing list: + // + // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1 + // + // with a blog post: + // + // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/ + // + // and download of the original plugin: + // + // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js + ////////// + + // called with scope of series + function computeHermiteSmoothedData (gd) { + var smooth = this.renderer.smooth; + var tension = this.renderer.tension; + var dim = this.canvas.getWidth(); + var xp = this._xaxis.series_p2u; + var yp = this._yaxis.series_p2u; + var steps =null; + var _steps = null; + var a = null; + var a1 = null; + var a2 = null; + var slope = null; + var slope2 = null; + var temp = null; + var t, s, h1, h2, h3, h4; + var TiX, TiY, Ti1X, Ti1Y; + var pX, pY, p; + var sd = []; + var spd = []; + var dist = gd.length/dim; + var min, max, stretch, scale, shift; + var _smoothedData = []; + var _smoothedPlotData = []; + if (!isNaN(parseFloat(smooth))) { + steps = parseFloat(smooth); + } + else { + steps = getSteps(dist, 0.5); + } + if (!isNaN(parseFloat(tension))) { + tension = parseFloat(tension); + } + + for (var i=0, l = gd.length-1; i < l; i++) { + + if (tension === null) { + slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0])); + + min = 0.3; + max = 0.6; + stretch = (max - min)/2.0; + scale = 2.5; + shift = -1.4; + + temp = slope/scale + shift; + + a1 = stretch * tanh(temp) - stretch * tanh(shift) + min; + + // if have both left and right line segments, will use minimum tension. + if (i > 0) { + slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0])); + } + temp = slope2/scale + shift; + + a2 = stretch * tanh(temp) - stretch * tanh(shift) + min; + + a = (a1 + a2)/2.0; + + } + else { + a = tension; + } + for (t=0; t < steps; t++) { + s = t / steps; + h1 = (1 + 2*s)*Math.pow((1-s),2); + h2 = s*Math.pow((1-s),2); + h3 = Math.pow(s,2)*(3-2*s); + h4 = Math.pow(s,2)*(s-1); + + if (gd[i-1]) { + TiX = a * (gd[i+1][0] - gd[i-1][0]); + TiY = a * (gd[i+1][1] - gd[i-1][1]); + } else { + TiX = a * (gd[i+1][0] - gd[i][0]); + TiY = a * (gd[i+1][1] - gd[i][1]); + } + if (gd[i+2]) { + Ti1X = a * (gd[i+2][0] - gd[i][0]); + Ti1Y = a * (gd[i+2][1] - gd[i][1]); + } else { + Ti1X = a * (gd[i+1][0] - gd[i][0]); + Ti1Y = a * (gd[i+1][1] - gd[i][1]); + } + + pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X; + pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y; + p = [pX, pY]; + + _smoothedData.push(p); + _smoothedPlotData.push([xp(pX), yp(pY)]); + } + } + _smoothedData.push(gd[l]); + _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]); + + return [_smoothedData, _smoothedPlotData]; + } + + // setGridData + // converts the user data values to grid coordinates and stores them + // in the gridData array. + // Called with scope of a series. + $.jqplot.LineRenderer.prototype.setGridData = function(plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var data = this._plotData; + var pdata = this._prevPlotData; + this.gridData = []; + this._prevGridData = []; + this.renderer._smoothedData = []; + this.renderer._smoothedPlotData = []; + this.renderer._hiBandGridData = []; + this.renderer._lowBandGridData = []; + this.renderer._hiBandSmoothedData = []; + this.renderer._lowBandSmoothedData = []; + var bands = this.renderer.bands; + var hasNull = false; + for (var i=0, l=data.length; i < l; i++) { + // if not a line series or if no nulls in data, push the converted point onto the array. + if (data[i][0] != null && data[i][1] != null) { + this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); + } + // else if there is a null, preserve it. + else if (data[i][0] == null) { + hasNull = true; + this.gridData.push([null, yp.call(this._yaxis, data[i][1])]); + } + else if (data[i][1] == null) { + hasNull = true; + this.gridData.push([xp.call(this._xaxis, data[i][0]), null]); + } + // if not a line series or if no nulls in data, push the converted point onto the array. + if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) { + this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]); + } + // else if there is a null, preserve it. + else if (pdata[i] != null && pdata[i][0] == null) { + this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]); + } + else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) { + this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]); + } + } + + // don't do smoothing or bands on broken lines. + if (hasNull) { + this.renderer.smooth = false; + if (this._type === 'line') { + bands.show = false; + } + } + + if (this._type === 'line' && bands.show) { + for (var i=0, l=bands.hiData.length; i<l; i++) { + this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); + } + for (var i=0, l=bands.lowData.length; i<l; i++) { + this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); + } + } + + // calculate smoothed data if enough points and no nulls + if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) { + var ret; + if (this.renderer.constrainSmoothing) { + ret = computeConstrainedSmoothedData.call(this, this.gridData); + this.renderer._smoothedData = ret[0]; + this.renderer._smoothedPlotData = ret[1]; + + if (bands.show) { + ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); + this.renderer._hiBandSmoothedData = ret[0]; + ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); + this.renderer._lowBandSmoothedData = ret[0]; + } + + ret = null; + } + else { + ret = computeHermiteSmoothedData.call(this, this.gridData); + this.renderer._smoothedData = ret[0]; + this.renderer._smoothedPlotData = ret[1]; + + if (bands.show) { + ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); + this.renderer._hiBandSmoothedData = ret[0]; + ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); + this.renderer._lowBandSmoothedData = ret[0]; + } + + ret = null; + } + } + }; + + // makeGridData + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var gd = []; + var pgd = []; + this.renderer._smoothedData = []; + this.renderer._smoothedPlotData = []; + this.renderer._hiBandGridData = []; + this.renderer._lowBandGridData = []; + this.renderer._hiBandSmoothedData = []; + this.renderer._lowBandSmoothedData = []; + var bands = this.renderer.bands; + var hasNull = false; + for (var i=0; i<data.length; i++) { + // if not a line series or if no nulls in data, push the converted point onto the array. + if (data[i][0] != null && data[i][1] != null) { + gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); + } + // else if there is a null, preserve it. + else if (data[i][0] == null) { + hasNull = true; + gd.push([null, yp.call(this._yaxis, data[i][1])]); + } + else if (data[i][1] == null) { + hasNull = true; + gd.push([xp.call(this._xaxis, data[i][0]), null]); + } + } + + // don't do smoothing or bands on broken lines. + if (hasNull) { + this.renderer.smooth = false; + if (this._type === 'line') { + bands.show = false; + } + } + + if (this._type === 'line' && bands.show) { + for (var i=0, l=bands.hiData.length; i<l; i++) { + this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); + } + for (var i=0, l=bands.lowData.length; i<l; i++) { + this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); + } + } + + if (this._type === 'line' && this.renderer.smooth && gd.length > 2) { + var ret; + if (this.renderer.constrainSmoothing) { + ret = computeConstrainedSmoothedData.call(this, gd); + this.renderer._smoothedData = ret[0]; + this.renderer._smoothedPlotData = ret[1]; + + if (bands.show) { + ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); + this.renderer._hiBandSmoothedData = ret[0]; + ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); + this.renderer._lowBandSmoothedData = ret[0]; + } + + ret = null; + } + else { + ret = computeHermiteSmoothedData.call(this, gd); + this.renderer._smoothedData = ret[0]; + this.renderer._smoothedPlotData = ret[1]; + + if (bands.show) { + ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); + this.renderer._hiBandSmoothedData = ret[0]; + ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); + this.renderer._lowBandSmoothedData = ret[0]; + } + + ret = null; + } + } + return gd; + }; + + + // called within scope of series. + $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) { + var i; + // get a copy of the options, so we don't modify the original object. + var opts = $.extend(true, {}, options); + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; + var xmin, ymin, xmax, ymax; + ctx.save(); + if (gd.length) { + if (showLine) { + // if we fill, we'll have to add points to close the curve. + if (fill) { + if (this.fillToZero) { + // have to break line up into shapes at axis crossings + var negativeColor = this.negativeColor; + if (! this.useNegativeColors) { + negativeColor = opts.fillStyle; + } + var isnegative = false; + var posfs = opts.fillStyle; + + // if stoking line as well as filling, get a copy of line data. + if (fillAndStroke) { + var fasgd = gd.slice(0); + } + // if not stacked, fill down to axis + if (this.index == 0 || !this._stack) { + + var tempgd = []; + var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData; + this._areaPoints = []; + var pyzero = this._yaxis.series_u2p(this.fillToValue); + var pxzero = this._xaxis.series_u2p(this.fillToValue); + + opts.closePath = true; + + if (this.fillAxis == 'y') { + tempgd.push([gd[0][0], pyzero]); + this._areaPoints.push([gd[0][0], pyzero]); + + for (var i=0; i<gd.length-1; i++) { + tempgd.push(gd[i]); + this._areaPoints.push(gd[i]); + // do we have an axis crossing? + if (pd[i][1] * pd[i+1][1] <= 0) { + if (pd[i][1] < 0) { + isnegative = true; + opts.fillStyle = negativeColor; + } + else { + isnegative = false; + opts.fillStyle = posfs; + } + + var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]); + tempgd.push([xintercept, pyzero]); + this._areaPoints.push([xintercept, pyzero]); + // now draw this shape and shadow. + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, tempgd, opts); + } + this.renderer.shapeRenderer.draw(ctx, tempgd, opts); + // now empty temp array and continue + tempgd = [[xintercept, pyzero]]; + // this._areaPoints = [[xintercept, pyzero]]; + } + } + if (pd[gd.length-1][1] < 0) { + isnegative = true; + opts.fillStyle = negativeColor; + } + else { + isnegative = false; + opts.fillStyle = posfs; + } + tempgd.push(gd[gd.length-1]); + this._areaPoints.push(gd[gd.length-1]); + tempgd.push([gd[gd.length-1][0], pyzero]); + this._areaPoints.push([gd[gd.length-1][0], pyzero]); + } + // now draw the last area. + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, tempgd, opts); + } + this.renderer.shapeRenderer.draw(ctx, tempgd, opts); + + + // var gridymin = this._yaxis.series_u2p(0); + // // IE doesn't return new length on unshift + // gd.unshift([gd[0][0], gridymin]); + // len = gd.length; + // gd.push([gd[len - 1][0], gridymin]); + } + // if stacked, fill to line below + else { + var prev = this._prevGridData; + for (var i=prev.length; i>0; i--) { + gd.push(prev[i-1]); + // this._areaPoints.push(prev[i-1]); + } + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, gd, opts); + } + this._areaPoints = gd; + this.renderer.shapeRenderer.draw(ctx, gd, opts); + } + } + ///////////////////////// + // Not filled to zero + //////////////////////// + else { + // if stoking line as well as filling, get a copy of line data. + if (fillAndStroke) { + var fasgd = gd.slice(0); + } + // if not stacked, fill down to axis + if (this.index == 0 || !this._stack) { + // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2; + var gridymin = ctx.canvas.height; + // IE doesn't return new length on unshift + gd.unshift([gd[0][0], gridymin]); + var len = gd.length; + gd.push([gd[len - 1][0], gridymin]); + } + // if stacked, fill to line below + else { + var prev = this._prevGridData; + for (var i=prev.length; i>0; i--) { + gd.push(prev[i-1]); + } + } + this._areaPoints = gd; + + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, gd, opts); + } + + this.renderer.shapeRenderer.draw(ctx, gd, opts); + } + if (fillAndStroke) { + var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false}); + this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts); + ////////// + // TODO: figure out some way to do shadows nicely + // if (shadow) { + // this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts); + // } + // now draw the markers + if (this.markerRenderer.show) { + if (this.renderer.smooth) { + fasgd = this.gridData; + } + for (i=0; i<fasgd.length; i++) { + this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions); + } + } + } + } + else { + + if (this.renderer.bands.show) { + var bdat; + var bopts = $.extend(true, {}, opts); + if (this.renderer.bands.showLines) { + bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData; + this.renderer.shapeRenderer.draw(ctx, bdat, opts); + bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData; + this.renderer.shapeRenderer.draw(ctx, bdat, bopts); + } + + if (this.renderer.bands.fill) { + if (this.renderer.smooth) { + bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse()); + } + else { + bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse()); + } + this._areaPoints = bdat; + bopts.closePath = true; + bopts.fill = true; + bopts.fillStyle = this.renderer.bands.fillColor; + this.renderer.shapeRenderer.draw(ctx, bdat, bopts); + } + } + + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, gd, opts); + } + + this.renderer.shapeRenderer.draw(ctx, gd, opts); + } + } + // calculate the bounding box + var xmin = xmax = ymin = ymax = null; + for (i=0; i<this._areaPoints.length; i++) { + var p = this._areaPoints[i]; + if (xmin > p[0] || xmin == null) { + xmin = p[0]; + } + if (ymax < p[1] || ymax == null) { + ymax = p[1]; + } + if (xmax < p[0] || xmax == null) { + xmax = p[0]; + } + if (ymin > p[1] || ymin == null) { + ymin = p[1]; + } + } + + if (this.type === 'line' && this.renderer.bands.show) { + ymax = this._yaxis.series_u2p(this.renderer.bands._min); + ymin = this._yaxis.series_u2p(this.renderer.bands._max); + } + + this._boundingBox = [[xmin, ymax], [xmax, ymin]]; + + // now draw the markers + if (this.markerRenderer.show && !fill) { + if (this.renderer.smooth) { + gd = this.gridData; + } + for (i=0; i<gd.length; i++) { + if (gd[i][0] != null && gd[i][1] != null) { + this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions); + } + } + } + } + + ctx.restore(); + }; + + $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) { + // This is a no-op, shadows drawn with lines. + }; + + // called with scope of plot. + // make sure to not leave anything highlighted. + function postInit(target, data, options) { + for (var i=0; i<this.series.length; i++) { + if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) { + // don't allow mouseover and mousedown at same time. + if (this.series[i].highlightMouseOver) { + this.series[i].highlightMouseDown = false; + } + } + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) { + this.plugins.lineRenderer.highlightCanvas.resetCanvas(); + this.plugins.lineRenderer.highlightCanvas = null; + } + + this.plugins.lineRenderer.highlightedSeriesIndex = null; + this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this)); + this.plugins.lineRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + function highlight (plot, sidx, pidx, points) { + var s = plot.series[sidx]; + var canvas = plot.plugins.lineRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.lineRenderer.highlightedSeriesIndex = sidx; + var opts = {fillStyle: s.highlightColor}; + if (s.type === 'line' && s.renderer.bands.show) { + opts.fill = true; + opts.closePath = true; + } + s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); + canvas = null; + } + + function unhighlight (plot) { + var canvas = plot.plugins.lineRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.lineRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + canvas = null; + } + + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + + // class: $.jqplot.LinearAxisRenderer + // The default jqPlot axis renderer, creating a numeric axis. + $.jqplot.LinearAxisRenderer = function() { + }; + + // called with scope of axis object. + $.jqplot.LinearAxisRenderer.prototype.init = function(options){ + // prop: breakPoints + // EXPERIMENTAL!! Use at your own risk! + // Works only with linear axes and the default tick renderer. + // Array of [start, stop] points to create a broken axis. + // Broken axes have a "jump" in them, which is an immediate + // transition from a smaller value to a larger value. + // Currently, axis ticks MUST be manually assigned if using breakPoints + // by using the axis ticks array option. + this.breakPoints = null; + // prop: breakTickLabel + // Label to use at the axis break if breakPoints are specified. + this.breakTickLabel = "≈"; + // prop: drawBaseline + // True to draw the axis baseline. + this.drawBaseline = true; + // prop: baselineWidth + // width of the baseline in pixels. + this.baselineWidth = null; + // prop: baselineColor + // CSS color spec for the baseline. + this.baselineColor = null; + // prop: forceTickAt0 + // This will ensure that there is always a tick mark at 0. + // If data range is strictly positive or negative, + // this will force 0 to be inside the axis bounds unless + // the appropriate axis pad (pad, padMin or padMax) is set + // to 0, then this will force an axis min or max value at 0. + // This has know effect when any of the following options + // are set: autoscale, min, max, numberTicks or tickInterval. + this.forceTickAt0 = false; + // prop: forceTickAt100 + // This will ensure that there is always a tick mark at 100. + // If data range is strictly above or below 100, + // this will force 100 to be inside the axis bounds unless + // the appropriate axis pad (pad, padMin or padMax) is set + // to 0, then this will force an axis min or max value at 100. + // This has know effect when any of the following options + // are set: autoscale, min, max, numberTicks or tickInterval. + this.forceTickAt100 = false; + // prop: tickInset + // Controls the amount to inset the first and last ticks from + // the edges of the grid, in multiples of the tick interval. + // 0 is no inset, 0.5 is one half a tick interval, 1 is a full + // tick interval, etc. + this.tickInset = 0; + // prop: minorTicks + // Number of ticks to add between "major" ticks. + // Major ticks are ticks supplied by user or auto computed. + // Minor ticks cannot be created by user. + this.minorTicks = 0; + // prop: alignTicks + // true to align tick marks across opposed axes + // such as from the y2axis to yaxis. + this.alignTicks = false; + this._autoFormatString = ''; + this._overrideFormatString = false; + this._scalefact = 1.0; + $.extend(true, this, options); + if (this.breakPoints) { + if (!$.isArray(this.breakPoints)) { + this.breakPoints = null; + } + else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) { + this.breakPoints = null; + } + } + if (this.numberTicks != null && this.numberTicks < 2) { + this.numberTicks = 2; + } + this.resetDataBounds(); + }; + + // called with scope of axis + $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) { + if (this.show) { + // populate the axis label and value properties. + // createTicks is a method on the renderer, but + // call it within the scope of the axis. + this.renderer.createTicks.call(this, plot); + // fill a div with axes labels in the right direction. + // Need to pregenerate each axis to get its bounds and + // position it and the labels correctly on the plot. + var dim=0; + var temp; + // Added for theming. + if (this._elem) { + // Memory Leaks patch + //this._elem.empty(); + this._elem.emptyForce(); + this._elem = null; + } + + this._elem = $(document.createElement('div')); + this._elem.addClass('jqplot-axis jqplot-'+this.name); + this._elem.css('position', 'absolute'); + + + if (this.name == 'xaxis' || this.name == 'x2axis') { + this._elem.width(this._plotDimensions.width); + } + else { + this._elem.height(this._plotDimensions.height); + } + + // create a _label object. + this.labelOptions.axis = this.name; + this._label = new this.labelRenderer(this.labelOptions); + if (this._label.show) { + var elem = this._label.draw(ctx, plot); + elem.appendTo(this._elem); + elem = null; + } + + var t = this._ticks; + var tick; + for (var i=0; i<t.length; i++) { + tick = t[i]; + if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + this._elem.append(tick.draw(ctx, plot)); + } + } + tick = null; + t = null; + } + return this._elem; + }; + + // called with scope of an axis + $.jqplot.LinearAxisRenderer.prototype.reset = function() { + this.min = this._options.min; + this.max = this._options.max; + this.tickInterval = this._options.tickInterval; + this.numberTicks = this._options.numberTicks; + this._autoFormatString = ''; + if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { + this.tickOptions.formatString = ''; + } + + // this._ticks = this.__ticks; + }; + + // called with scope of axis + $.jqplot.LinearAxisRenderer.prototype.set = function() { + var dim = 0; + var temp; + var w = 0; + var h = 0; + var lshow = (this._label == null) ? false : this._label.show; + if (this.show) { + var t = this._ticks; + var tick; + for (var i=0; i<t.length; i++) { + tick = t[i]; + if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + temp = tick._elem.outerHeight(true); + } + else { + temp = tick._elem.outerWidth(true); + } + if (temp > dim) { + dim = temp; + } + } + } + tick = null; + t = null; + + if (lshow) { + w = this._label._elem.outerWidth(true); + h = this._label._elem.outerHeight(true); + } + if (this.name == 'xaxis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); + } + else if (this.name == 'x2axis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); + } + else if (this.name == 'yaxis') { + dim = dim + w; + this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + else { + dim = dim + w; + this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + } + }; + + // called with scope of axis + $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) { + // we're are operating on an axis here + var ticks = this._ticks; + var userTicks = this.ticks; + var name = this.name; + // databounds were set on axis initialization. + var db = this._dataBounds; + var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; + var interval; + var min, max; + var pos1, pos2; + var tt, i; + // get a copy of user's settings for min/max. + var userMin = this.min; + var userMax = this.max; + var userNT = this.numberTicks; + var userTI = this.tickInterval; + + var threshold = 30; + this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0; + + // if we already have ticks, use them. + // ticks must be in order of increasing value. + + if (userTicks.length) { + // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed + for (i=0; i<userTicks.length; i++){ + var ut = userTicks[i]; + var t = new this.tickRenderer(this.tickOptions); + if ($.isArray(ut)) { + t.value = ut[0]; + if (this.breakPoints) { + if (ut[0] == this.breakPoints[0]) { + t.label = this.breakTickLabel; + t._breakTick = true; + t.showGridline = false; + t.showMark = false; + } + else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) { + t.show = false; + t.showGridline = false; + t.label = ut[1]; + } + else { + t.label = ut[1]; + } + } + else { + t.label = ut[1]; + } + t.setTick(ut[0], this.name); + this._ticks.push(t); + } + + else if ($.isPlainObject(ut)) { + $.extend(true, t, ut); + t.axis = this.name; + this._ticks.push(t); + } + + else { + t.value = ut; + if (this.breakPoints) { + if (ut == this.breakPoints[0]) { + t.label = this.breakTickLabel; + t._breakTick = true; + t.showGridline = false; + t.showMark = false; + } + else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) { + t.show = false; + t.showGridline = false; + } + } + t.setTick(ut, this.name); + this._ticks.push(t); + } + } + this.numberTicks = userTicks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this.numberTicks-1].value; + this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); + } + + // we don't have any ticks yet, let's make some! + else { + if (name == 'xaxis' || name == 'x2axis') { + dim = this._plotDimensions.width; + } + else { + dim = this._plotDimensions.height; + } + + var _numberTicks = this.numberTicks; + + // if aligning this axis, use number of ticks from previous axis. + // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? + if (this.alignTicks) { + if (this.name === 'x2axis' && plot.axes.xaxis.show) { + _numberTicks = plot.axes.xaxis.numberTicks; + } + else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) { + _numberTicks = plot.axes.yaxis.numberTicks; + } + } + + min = ((this.min != null) ? this.min : db.min); + max = ((this.max != null) ? this.max : db.max); + + var range = max - min; + var rmin, rmax; + var temp; + + if (this.tickOptions == null || !this.tickOptions.formatString) { + this._overrideFormatString = true; + } + + // Doing complete autoscaling + if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) { + // Check if user must have tick at 0 or 100 and ensure they are in range. + // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range. + if (this.forceTickAt0) { + if (min > 0) { + min = 0; + } + if (max < 0) { + max = 0; + } + } + + if (this.forceTickAt100) { + if (min > 100) { + min = 100; + } + if (max < 100) { + max = 100; + } + } + + var keepMin = false, + keepMax = false; + + if (this.min != null) { + keepMin = true; + } + + else if (this.max != null) { + keepMax = true; + } + + // var threshold = 30; + // var tdim = Math.max(dim, threshold+1); + // this._scalefact = (tdim-threshold)/300.0; + var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); + // calculate a padded max and min, points should be less than these + // so that they aren't too close to the edges of the plot. + // User can adjust how much padding is allowed with pad, padMin and PadMax options. + // If min or max is set, don't pad that end of axis. + var tumin = (this.min != null) ? min : min + range*(this.padMin - 1); + var tumax = (this.max != null) ? max : max - range*(this.padMax - 1); + + // if they're equal, we shouldn't have to do anything, right? + // if (min <=tumin || max >= tumax) { + if (min <tumin || max > tumax) { + tumin = (this.min != null) ? min : min - range*(this.padMin - 1); + tumax = (this.max != null) ? max : max + range*(this.padMax - 1); + ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax); + } + + this.min = ret[0]; + this.max = ret[1]; + // if numberTicks specified, it should return the same. + this.numberTicks = ret[2]; + this._autoFormatString = ret[3]; + this.tickInterval = ret[4]; + } + + // User has specified some axis scale related option, can use auto algorithm + else { + + // if min and max are same, space them out a bit + if (min == max) { + var adj = 0.05; + if (min > 0) { + adj = Math.max(Math.log(min)/Math.LN10, 0.05); + } + min -= adj; + max += adj; + } + + // autoscale. Can't autoscale if min or max is supplied. + // Will use numberTicks and tickInterval if supplied. Ticks + // across multiple axes may not line up depending on how + // bars are to be plotted. + if (this.autoscale && this.min == null && this.max == null) { + var rrange, ti, margin; + var forceMinZero = false; + var forceZeroLine = false; + var intervals = {min:null, max:null, average:null, stddev:null}; + // if any series are bars, or if any are fill to zero, and if this + // is the axis to fill toward, check to see if we can start axis at zero. + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name; + // check to see if this is the fill axis + if (this.name == faname) { + var vals = s._plotValues[s.fillAxis]; + var vmin = vals[0]; + var vmax = vals[0]; + for (var j=1; j<vals.length; j++) { + if (vals[j] < vmin) { + vmin = vals[j]; + } + else if (vals[j] > vmax) { + vmax = vals[j]; + } + } + var dp = (vmax - vmin) / vmax; + // is this sries a bar? + if (s.renderer.constructor == $.jqplot.BarRenderer) { + // if no negative values and could also check range. + if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { + forceMinZero = true; + } + else { + forceMinZero = false; + if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) { + forceZeroLine = true; + } + else { + forceZeroLine = false; + } + } + } + + // if not a bar and filling, use appropriate method. + else if (s.fill) { + if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { + forceMinZero = true; + } + else if (vmin < 0 && vmax > 0 && s.fillToZero) { + forceMinZero = false; + forceZeroLine = true; + } + else { + forceMinZero = false; + forceZeroLine = false; + } + } + + // if not a bar and not filling, only change existing state + // if it doesn't make sense + else if (vmin < 0) { + forceMinZero = false; + } + } + } + + // check if we need make axis min at 0. + if (forceMinZero) { + // compute number of ticks + this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); + this.min = 0; + userMin = 0; + // what order is this range? + // what tick interval does that give us? + ti = max/(this.numberTicks-1); + temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); + if (ti/temp == parseInt(ti/temp, 10)) { + ti += temp; + } + this.tickInterval = Math.ceil(ti/temp) * temp; + this.max = this.tickInterval * (this.numberTicks - 1); + } + + // check if we need to make sure there is a tick at 0. + else if (forceZeroLine) { + // compute number of ticks + this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); + var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1)); + var ntmax = this.numberTicks - 1 - ntmin; + ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax)); + temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); + this.tickInterval = Math.ceil(ti/temp) * temp; + this.max = this.tickInterval * ntmax; + this.min = -this.tickInterval * ntmin; + } + + // if nothing else, do autoscaling which will try to line up ticks across axes. + else { + if (this.numberTicks == null){ + if (this.tickInterval) { + this.numberTicks = 3 + Math.ceil(range / this.tickInterval); + } + else { + this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); + } + } + + if (this.tickInterval == null) { + // get a tick interval + ti = range/(this.numberTicks - 1); + + if (ti < 1) { + temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); + } + else { + temp = 1; + } + this.tickInterval = Math.ceil(ti*temp*this.pad)/temp; + } + else { + temp = 1 / this.tickInterval; + } + + // try to compute a nicer, more even tick interval + // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10)); + // this.tickInterval = Math.ceil(ti/temp) * temp; + rrange = this.tickInterval * (this.numberTicks - 1); + margin = (rrange - range)/2; + + if (this.min == null) { + this.min = Math.floor(temp*(min-margin))/temp; + } + if (this.max == null) { + this.max = this.min + rrange; + } + } + + // Compute a somewhat decent format string if it is needed. + // get precision of interval and determine a format string. + var sf = $.jqplot.getSignificantFigures(this.tickInterval); + + var fstr; + + // if we have only a whole number, use integer formatting + if (sf.digitsLeft >= sf.significantDigits) { + fstr = '%d'; + } + + else { + var temp = Math.max(0, 5 - sf.digitsLeft); + temp = Math.min(temp, sf.digitsRight); + fstr = '%.'+ temp + 'f'; + } + + this._autoFormatString = fstr; + } + + // Use the default algorithm which pads each axis to make the chart + // centered nicely on the grid. + else { + + rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1); + rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1); + range = rmax - rmin; + + if (this.numberTicks == null){ + // if tickInterval is specified by user, we will ignore computed maximum. + // max will be equal or greater to fit even # of ticks. + if (this.tickInterval != null) { + this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1; + } + else if (dim > 100) { + this.numberTicks = parseInt(3+(dim-100)/75, 10); + } + else { + this.numberTicks = 2; + } + } + + if (this.tickInterval == null) { + this.tickInterval = range / (this.numberTicks-1); + } + + if (this.max == null) { + rmax = rmin + this.tickInterval*(this.numberTicks - 1); + } + if (this.min == null) { + rmin = rmax - this.tickInterval*(this.numberTicks - 1); + } + + // get precision of interval and determine a format string. + var sf = $.jqplot.getSignificantFigures(this.tickInterval); + + var fstr; + + // if we have only a whole number, use integer formatting + if (sf.digitsLeft >= sf.significantDigits) { + fstr = '%d'; + } + + else { + var temp = Math.max(0, 5 - sf.digitsLeft); + temp = Math.min(temp, sf.digitsRight); + fstr = '%.'+ temp + 'f'; + } + + + this._autoFormatString = fstr; + + this.min = rmin; + this.max = rmax; + } + + if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') { + // fix for misleading tick display with small range and low precision. + range = this.max - this.min; + // figure out precision + var temptick = new this.tickRenderer(this.tickOptions); + // use the tick formatString or, the default. + var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; + var fs = fs.match($.jqplot.sprintf.regex)[0]; + var precision = 0; + if (fs) { + if (fs.search(/[fFeEgGpP]/) > -1) { + var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/); + if (m) { + precision = parseInt(m[1], 10); + } + else { + precision = 6; + } + } + else if (fs.search(/[di]/) > -1) { + precision = 0; + } + // fact will be <= 1; + var fact = Math.pow(10, -precision); + if (this.tickInterval < fact) { + // need to correct underrange + if (userNT == null && userTI == null) { + this.tickInterval = fact; + if (userMax == null && userMin == null) { + // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact; + this.min = Math.floor(this._dataBounds.min/fact) * fact; + if (this.min == this._dataBounds.min) { + this.min = this._dataBounds.min - this.tickInterval; + } + // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact; + this.max = Math.ceil(this._dataBounds.max/fact) * fact; + if (this.max == this._dataBounds.max) { + this.max = this._dataBounds.max + this.tickInterval; + } + var n = (this.max - this.min)/this.tickInterval; + n = n.toFixed(11); + n = Math.ceil(n); + this.numberTicks = n + 1; + } + else if (userMax == null) { + // add one tick for top of range. + var n = (this._dataBounds.max - this.min) / this.tickInterval; + n = n.toFixed(11); + this.numberTicks = Math.ceil(n) + 2; + this.max = this.min + this.tickInterval * (this.numberTicks-1); + } + else if (userMin == null) { + // add one tick for bottom of range. + var n = (this.max - this._dataBounds.min) / this.tickInterval; + n = n.toFixed(11); + this.numberTicks = Math.ceil(n) + 2; + this.min = this.max - this.tickInterval * (this.numberTicks-1); + } + else { + // calculate a number of ticks so max is within axis scale + this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1; + // if user's min and max don't fit evenly in ticks, adjust. + // This takes care of cases such as user min set to 0, max set to 3.5 but tick + // format string set to %d (integer ticks) + this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision); + this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision); + // this.max = this.min + this.tickInterval*(this.numberTicks-1); + this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1; + } + } + } + } + } + + } + + if (this._overrideFormatString && this._autoFormatString != '') { + this.tickOptions = this.tickOptions || {}; + this.tickOptions.formatString = this._autoFormatString; + } + + var t, to; + for (var i=0; i<this.numberTicks; i++){ + tt = this.min + i * this.tickInterval; + t = new this.tickRenderer(this.tickOptions); + // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); + + t.setTick(tt, this.name); + this._ticks.push(t); + + if (i < this.numberTicks - 1) { + for (var j=0; j<this.minorTicks; j++) { + tt += this.tickInterval/(this.minorTicks+1); + to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true}); + t = new this.tickRenderer(to); + this._ticks.push(t); + } + } + t = null; + } + } + + if (this.tickInset) { + this.min = this.min - this.tickInset * this.tickInterval; + this.max = this.max + this.tickInset * this.tickInterval; + } + + ticks = null; + }; + + // Used to reset just the values of the ticks and then repack, which will + // recalculate the positioning functions. It is assuemd that the + // number of ticks is the same and the values of the new array are at the + // proper interval. + // This method needs to be called with the scope of an axis object, like: + // + // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr); + // + $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) { + if ($.isArray(opts) && opts.length == this._ticks.length) { + var t; + for (var i=0; i<opts.length; i++) { + t = this._ticks[i]; + t.value = opts[i]; + t.label = t.formatter(t.formatString, opts[i]); + t.label = t.prefix + t.label; + t._elem.html(t.label); + } + t = null; + this.min = $.jqplot.arrayMin(opts); + this.max = $.jqplot.arrayMax(opts); + this.pack(); + } + // Not implemented yet. + // else if ($.isPlainObject(opts)) { + // + // } + }; + + // called with scope of axis + $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) { + // Add defaults for repacking from resetTickValues function. + pos = pos || {}; + offsets = offsets || this._offsets; + + var ticks = this._ticks; + var max = this.max; + var min = this.min; + var offmax = offsets.max; + var offmin = offsets.min; + var lshow = (this._label == null) ? false : this._label.show; + + for (var p in pos) { + this._elem.css(p, pos[p]); + } + + this._offsets = offsets; + // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. + var pixellength = offmax - offmin; + var unitlength = max - min; + + // point to unit and unit to point conversions references to Plot DOM element top left corner. + if (this.breakPoints) { + unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0]; + + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + this.u2p = function(u){ + if (u > this.breakPoints[0] && u < this.breakPoints[1]){ + u = this.breakPoints[0]; + } + if (u <= this.breakPoints[0]) { + return (u - min) * pixellength / unitlength + offmin; + } + else { + return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin; + } + }; + + if (this.name.charAt(0) == 'x'){ + this.series_u2p = function(u){ + if (u > this.breakPoints[0] && u < this.breakPoints[1]){ + u = this.breakPoints[0]; + } + if (u <= this.breakPoints[0]) { + return (u - min) * pixellength / unitlength; + } + else { + return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength; + } + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + else { + this.series_u2p = function(u){ + if (u > this.breakPoints[0] && u < this.breakPoints[1]){ + u = this.breakPoints[0]; + } + if (u >= this.breakPoints[1]) { + return (u - max) * pixellength / unitlength; + } + else { + return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength; + } + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + } + else { + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + this.u2p = function(u){ + return (u - min) * pixellength / unitlength + offmin; + }; + + if (this.name == 'xaxis' || this.name == 'x2axis'){ + this.series_u2p = function(u){ + return (u - min) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + else { + this.series_u2p = function(u){ + return (u - max) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + } + + if (this.show) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'xaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + if (temp * t.angle < 0) { + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + } + // position at start + else { + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + } + break; + case 'end': + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + case 'start': + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + break; + case 'middle': + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + default: + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + } + } + else { + shim = -t.getWidth()/2; + } + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('left', val); + t.pack(); + } + } + if (lshow) { + var w = this._label._elem.outerWidth(true); + this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); + if (this.name == 'xaxis') { + this._label._elem.css('bottom', '0px'); + } + else { + this._label._elem.css('top', '0px'); + } + this._label.pack(); + } + } + else { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'yaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + case 'end': + if (temp * t.angle < 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'start': + if (t.angle > 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'middle': + // if (t.angle > 0) { + // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + // } + // else { + // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + // } + shim = -t.getHeight()/2; + break; + default: + shim = -t.getHeight()/2; + break; + } + } + else { + shim = -t.getHeight()/2; + } + + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('top', val); + t.pack(); + } + } + if (lshow) { + var h = this._label._elem.outerHeight(true); + this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); + if (this.name == 'yaxis') { + this._label._elem.css('left', '0px'); + } + else { + this._label._elem.css('right', '0px'); + } + this._label.pack(); + } + } + } + + ticks = null; + }; + + + /** + * The following code was generaously given to me a while back by Scott Prahl. + * He did a good job at computing axes min, max and number of ticks for the + * case where the user has not set any scale related parameters (tickInterval, + * numberTicks, min or max). I had ignored this use case for a long time, + * focusing on the more difficult case where user has set some option controlling + * tick generation. Anyway, about time I got this into jqPlot. + * Thanks Scott!! + */ + + /** + * Copyright (c) 2010 Scott Prahl + * The next three routines are currently available for use in all personal + * or commercial projects under both the MIT and GPL version 2.0 licenses. + * This means that you can choose the license that best suits your project + * and use it accordingly. + */ + + // A good format string depends on the interval. If the interval is greater + // than 1 then there is no need to show any decimal digits. If it is < 1.0, then + // use the magnitude of the interval to determine the number of digits to show. + function bestFormatString (interval) + { + var fstr; + interval = Math.abs(interval); + if (interval >= 10) { + fstr = '%d'; + } + + else if (interval > 1) { + if (interval === parseInt(interval, 10)) { + fstr = '%d'; + } + else { + fstr = '%.1f'; + } + } + + else { + var expv = -Math.floor(Math.log(interval)/Math.LN10); + fstr = '%.' + expv + 'f'; + } + + return fstr; + } + + var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5]; + + var _getLowerFactor = function(f) { + var i = _factors.indexOf(f); + if (i > 0) { + return _factors[i-1]; + } + else { + return _factors[_factors.length - 1] / 100; + } + }; + + var _getHigherFactor = function(f) { + var i = _factors.indexOf(f); + if (i < _factors.length-1) { + return _factors[i+1]; + } + else { + return _factors[0] * 100; + } + }; + + // Given a fixed minimum and maximum and a target number ot ticks + // figure out the best interval and + // return min, max, number ticks, format string and tick interval + function bestConstrainedInterval(min, max, nttarget) { + // run through possible number to ticks and see which interval is best + var low = Math.floor(nttarget/2); + var hi = Math.ceil(nttarget*1.5); + var badness = Number.MAX_VALUE; + var r = (max - min); + var temp; + var sd; + var bestNT; + var gsf = $.jqplot.getSignificantFigures; + var fsd; + var fs; + var currentNT; + var bestPrec; + + for (var i=0, l=hi-low+1; i<l; i++) { + currentNT = low + i; + temp = r/(currentNT-1); + sd = gsf(temp); + + temp = Math.abs(nttarget - currentNT) + sd.digitsRight; + if (temp < badness) { + badness = temp; + bestNT = currentNT; + bestPrec = sd.digitsRight; + } + else if (temp === badness) { + // let nicer ticks trump number ot ticks + if (sd.digitsRight < bestPrec) { + bestNT = currentNT; + bestPrec = sd.digitsRight; + } + } + + } + + fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight)); + if (fsd === 0) { + fs = '%d'; + } + else { + fs = '%.' + fsd + 'f'; + } + temp = r / (bestNT - 1); + // min, max, number ticks, format string, tick interval + return [min, max, bestNT, fs, temp]; + } + + // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n + // it is based soley on the range and number of ticks. So if user specifies + // number of ticks, use this. + function bestInterval(range, numberTicks) { + numberTicks = numberTicks || 7; + var minimum = range / (numberTicks - 1); + var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10)); + var residual = minimum / magnitude; + var interval; + // "nicest" ranges are 1, 2, 5 or powers of these. + // for magnitudes below 1, only allow these. + if (magnitude < 1) { + if (residual > 5) { + interval = 10 * magnitude; + } + else if (residual > 2) { + interval = 5 * magnitude; + } + else if (residual > 1) { + interval = 2 * magnitude; + } + else { + interval = magnitude; + } + } + // for large ranges (whole integers), allow intervals like 3, 4 or powers of these. + // this helps a lot with poor choices for number of ticks. + else { + if (residual > 5) { + interval = 10 * magnitude; + } + else if (residual > 4) { + interval = 5 * magnitude; + } + else if (residual > 3) { + interval = 4 * magnitude; + } + else if (residual > 2) { + interval = 3 * magnitude; + } + else if (residual > 1) { + interval = 2 * magnitude; + } + else { + interval = magnitude; + } + } + + return interval; + } + + // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n + // it is based soley on the range of data, number of ticks must be computed later. + function bestLinearInterval(range, scalefact) { + scalefact = scalefact || 1; + var expv = Math.floor(Math.log(range)/Math.LN10); + var magnitude = Math.pow(10, expv); + // 0 < f < 10 + var f = range / magnitude; + var fact; + // for large plots, scalefact will decrease f and increase number of ticks. + // for small plots, scalefact will increase f and decrease number of ticks. + f = f/scalefact; + + // for large plots, smaller interval, more ticks. + if (f<=0.38) { + fact = 0.1; + } + else if (f<=1.6) { + fact = 0.2; + } + else if (f<=4.0) { + fact = 0.5; + } + else if (f<=8.0) { + fact = 1.0; + } + // for very small plots, larger interval, less ticks in number ticks + else if (f<=16.0) { + fact = 2; + } + else { + fact = 5; + } + + return fact*magnitude; + } + + function bestLinearComponents(range, scalefact) { + var expv = Math.floor(Math.log(range)/Math.LN10); + var magnitude = Math.pow(10, expv); + // 0 < f < 10 + var f = range / magnitude; + var interval; + var fact; + // for large plots, scalefact will decrease f and increase number of ticks. + // for small plots, scalefact will increase f and decrease number of ticks. + f = f/scalefact; + + // for large plots, smaller interval, more ticks. + if (f<=0.38) { + fact = 0.1; + } + else if (f<=1.6) { + fact = 0.2; + } + else if (f<=4.0) { + fact = 0.5; + } + else if (f<=8.0) { + fact = 1.0; + } + // for very small plots, larger interval, less ticks in number ticks + else if (f<=16.0) { + fact = 2; + } + // else if (f<=20.0) { + // fact = 3; + // } + // else if (f<=24.0) { + // fact = 4; + // } + else { + fact = 5; + } + + interval = fact * magnitude; + + return [interval, fact, magnitude]; + } + + // Given the min and max for a dataset, return suitable endpoints + // for the graphing, a good number for the number of ticks, and a + // format string so that extraneous digits are not displayed. + // returned is an array containing [min, max, nTicks, format] + $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) { + // Set to preserve EITHER min OR max. + // If min is preserved, max must be free. + keepMin = (keepMin === null) ? false : keepMin; + keepMax = (keepMax === null || keepMin) ? false : keepMax; + // if endpoints are equal try to include zero otherwise include one + if (axis_min === axis_max) { + axis_max = (axis_max) ? 0 : 1; + } + + scalefact = scalefact || 1.0; + + // make sure range is positive + if (axis_max < axis_min) { + var a = axis_max; + axis_max = axis_min; + axis_min = a; + } + + var r = []; + var ss = bestLinearInterval(axis_max - axis_min, scalefact); + + var gsf = $.jqplot.getSignificantFigures; + + if (numberTicks == null) { + + // Figure out the axis min, max and number of ticks + // the min and max will be some multiple of the tick interval, + // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the + // axis min is negative, 0 will be a tick. + if (!keepMin && !keepMax) { + r[0] = Math.floor(axis_min / ss) * ss; // min + r[1] = Math.ceil(axis_max / ss) * ss; // max + r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks + r[3] = bestFormatString(ss); // format string + r[4] = ss; // tick Interval + } + + else if (keepMin) { + r[0] = axis_min; // min + r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks + r[1] = axis_min + (r[2] - 1) * ss; // max + var digitsMin = gsf(axis_min).digitsRight; + var digitsSS = gsf(ss).digitsRight; + if (digitsMin < digitsSS) { + r[3] = bestFormatString(ss); // format string + } + else { + r[3] = '%.' + digitsMin + 'f'; + } + r[4] = ss; // tick Interval + } + + else if (keepMax) { + r[1] = axis_max; // max + r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks + r[0] = axis_max - (r[2] - 1) * ss; // min + var digitsMax = gsf(axis_max).digitsRight; + var digitsSS = gsf(ss).digitsRight; + if (digitsMax < digitsSS) { + r[3] = bestFormatString(ss); // format string + } + else { + r[3] = '%.' + digitsMax + 'f'; + } + r[4] = ss; // tick Interval + } + } + + else { + var tempr = []; + + // Figure out the axis min, max and number of ticks + // the min and max will be some multiple of the tick interval, + // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the + // axis min is negative, 0 will be a tick. + tempr[0] = Math.floor(axis_min / ss) * ss; // min + tempr[1] = Math.ceil(axis_max / ss) * ss; // max + tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks + tempr[3] = bestFormatString(ss); // format string + tempr[4] = ss; // tick Interval + + // first, see if we happen to get the right number of ticks + if (tempr[2] === numberTicks) { + r = tempr; + } + + else { + + var newti = bestInterval(tempr[1] - tempr[0], numberTicks); + + r[0] = tempr[0]; // min + r[2] = numberTicks; // number of ticks + r[4] = newti; // tick interval + r[3] = bestFormatString(newti); // format string + r[1] = r[0] + (r[2] - 1) * r[4]; // max + } + } + + return r; + }; + + $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval; + $.jqplot.LinearTickGenerator.bestInterval = bestInterval; + $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents; + $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval; + + + // class: $.jqplot.MarkerRenderer + // The default jqPlot marker renderer, rendering the points on the line. + $.jqplot.MarkerRenderer = function(options){ + // Group: Properties + + // prop: show + // whether or not to show the marker. + this.show = true; + // prop: style + // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare + this.style = 'filledCircle'; + // prop: lineWidth + // size of the line for non-filled markers. + this.lineWidth = 2; + // prop: size + // Size of the marker (diameter or circle, length of edge of square, etc.) + this.size = 9.0; + // prop: color + // color of marker. Will be set to color of series by default on init. + this.color = '#666666'; + // prop: shadow + // whether or not to draw a shadow on the line + this.shadow = true; + // prop: shadowAngle + // Shadow angle in degrees + this.shadowAngle = 45; + // prop: shadowOffset + // Shadow offset from line in pixels + this.shadowOffset = 1; + // prop: shadowDepth + // Number of times shadow is stroked, each stroke offset shadowOffset from the last. + this.shadowDepth = 3; + // prop: shadowAlpha + // Alpha channel transparency of shadow. 0 = transparent. + this.shadowAlpha = '0.07'; + // prop: shadowRenderer + // Renderer that will draws the shadows on the marker. + this.shadowRenderer = new $.jqplot.ShadowRenderer(); + // prop: shapeRenderer + // Renderer that will draw the marker. + this.shapeRenderer = new $.jqplot.ShapeRenderer(); + + $.extend(true, this, options); + }; + + $.jqplot.MarkerRenderer.prototype.init = function(options) { + $.extend(true, this, options); + var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true}; + if (this.style.indexOf('filled') != -1) { + sdopt.fill = true; + } + if (this.style.indexOf('ircle') != -1) { + sdopt.isarc = true; + sdopt.closePath = false; + } + this.shadowRenderer.init(sdopt); + + var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true}; + if (this.style.indexOf('filled') != -1) { + shopt.fill = true; + } + if (this.style.indexOf('ircle') != -1) { + shopt.isarc = true; + shopt.closePath = false; + } + this.shapeRenderer.init(shopt); + }; + + $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) { + var stretch = 1.2; + var dx = this.size/2/stretch; + var dy = this.size/2*stretch; + var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points); + } + this.shapeRenderer.draw(ctx, points, options); + }; + + $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) { + var stretch = 1.0; + var dx = this.size/2*stretch; + var dy = this.size/2*stretch; + var points1 = [[x, y-dy], [x, y+dy]]; + var points2 = [[x+dx, y], [x-dx, y]]; + var opts = $.extend(true, {}, this.options, {closePath:false}); + if (this.shadow) { + this.shadowRenderer.draw(ctx, points1, {closePath:false}); + this.shadowRenderer.draw(ctx, points2, {closePath:false}); + } + this.shapeRenderer.draw(ctx, points1, opts); + this.shapeRenderer.draw(ctx, points2, opts); + }; + + $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) { + var stretch = 1.0; + var dx = this.size/2*stretch; + var dy = this.size/2*stretch; + var opts = $.extend(true, {}, this.options, {closePath:false}); + var points1 = [[x-dx, y-dy], [x+dx, y+dy]]; + var points2 = [[x-dx, y+dy], [x+dx, y-dy]]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points1, {closePath:false}); + this.shadowRenderer.draw(ctx, points2, {closePath:false}); + } + this.shapeRenderer.draw(ctx, points1, opts); + this.shapeRenderer.draw(ctx, points2, opts); + }; + + $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) { + var stretch = 1.0; + var dx = this.size/2*stretch; + var dy = this.size/2*stretch; + var points = [[x-dx, y], [x+dx, y]]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points); + } + this.shapeRenderer.draw(ctx, points, options); + }; + + $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) { + var points = [p1, p2]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points); + } + this.shapeRenderer.draw(ctx, points, options); + }; + + $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) { + var stretch = 1.0; + var dx = this.size/2/stretch; + var dy = this.size/2*stretch; + var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points); + } + this.shapeRenderer.draw(ctx, points, options); + }; + + $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) { + var radius = this.size/2; + var end = 2*Math.PI; + var points = [x, y, radius, 0, end, true]; + if (this.shadow) { + this.shadowRenderer.draw(ctx, points); + } + this.shapeRenderer.draw(ctx, points, options); + }; + + $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) { + options = options || {}; + // hack here b/c shape renderer uses canvas based color style options + // and marker uses css style names. + if (options.show == null || options.show != false) { + if (options.color && !options.fillStyle) { + options.fillStyle = options.color; + } + if (options.color && !options.strokeStyle) { + options.strokeStyle = options.color; + } + switch (this.style) { + case 'diamond': + this.drawDiamond(x,y,ctx, false, options); + break; + case 'filledDiamond': + this.drawDiamond(x,y,ctx, true, options); + break; + case 'circle': + this.drawCircle(x,y,ctx, false, options); + break; + case 'filledCircle': + this.drawCircle(x,y,ctx, true, options); + break; + case 'square': + this.drawSquare(x,y,ctx, false, options); + break; + case 'filledSquare': + this.drawSquare(x,y,ctx, true, options); + break; + case 'x': + this.drawX(x,y,ctx, true, options); + break; + case 'plus': + this.drawPlus(x,y,ctx, true, options); + break; + case 'dash': + this.drawDash(x,y,ctx, true, options); + break; + case 'line': + this.drawLine(x, y, ctx, false, options); + break; + default: + // this.drawDiamond(x,y,ctx, false, options); + break; + } + } + }; + + // class: $.jqplot.shadowRenderer + // The default jqPlot shadow renderer, rendering shadows behind shapes. + $.jqplot.ShadowRenderer = function(options){ + // Group: Properties + + // prop: angle + // Angle of the shadow in degrees. Measured counter-clockwise from the x axis. + this.angle = 45; + // prop: offset + // Pixel offset at the given shadow angle of each shadow stroke from the last stroke. + this.offset = 1; + // prop: alpha + // alpha transparency of shadow stroke. + this.alpha = 0.07; + // prop: lineWidth + // width of the shadow line stroke. + this.lineWidth = 1.5; + // prop: lineJoin + // How line segments of the shadow are joined. + this.lineJoin = 'miter'; + // prop: lineCap + // how ends of the shadow line are rendered. + this.lineCap = 'round'; + // prop; closePath + // whether line path segment is closed upon itself. + this.closePath = false; + // prop: fill + // whether to fill the shape. + this.fill = false; + // prop: depth + // how many times the shadow is stroked. Each stroke will be offset by offset at angle degrees. + this.depth = 3; + this.strokeStyle = 'rgba(0,0,0,0.1)'; + // prop: isarc + // whether the shadow is an arc or not. + this.isarc = false; + + $.extend(true, this, options); + }; + + $.jqplot.ShadowRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + // function: draw + // draws an transparent black (i.e. gray) shadow. + // + // ctx - canvas drawing context + // points - array of points or [x, y, radius, start angle (rad), end angle (rad)] + $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) { + ctx.save(); + var opts = (options != null) ? options : {}; + var fill = (opts.fill != null) ? opts.fill : this.fill; + var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; + var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; + var offset = (opts.offset != null) ? opts.offset : this.offset; + var alpha = (opts.alpha != null) ? opts.alpha : this.alpha; + var depth = (opts.depth != null) ? opts.depth : this.depth; + var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; + var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; + ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth; + ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin; + ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap; + ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')'; + ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')'; + for (var j=0; j<depth; j++) { + var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); + ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset); + ctxPattern.beginPath(); + if (isarc) { + ctx.arc(points[0], points[1], points[2], points[3], points[4], true); + } + else if (fillRect) { + if (fillRect) { + ctx.fillRect(points[0], points[1], points[2], points[3]); + } + } + else if (points && points.length){ + var move = true; + for (var i=0; i<points.length; i++) { + // skip to the first non-null point and move to it. + if (points[i][0] != null && points[i][1] != null) { + if (move) { + ctxPattern.moveTo(points[i][0], points[i][1]); + move = false; + } + else { + ctxPattern.lineTo(points[i][0], points[i][1]); + } + } + else { + move = true; + } + } + + } + if (closePath) { + ctxPattern.closePath(); + } + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + } + ctx.restore(); + }; + + // class: $.jqplot.shapeRenderer + // The default jqPlot shape renderer. Given a set of points will + // plot them and either stroke a line (fill = false) or fill them (fill = true). + // If a filled shape is desired, closePath = true must also be set to close + // the shape. + $.jqplot.ShapeRenderer = function(options){ + + this.lineWidth = 1.5; + // prop: linePattern + // line pattern 'dashed', 'dotted', 'solid', some combination + // of '-' and '.' characters such as '.-.' or a numerical array like + // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, + // [1, 10, 20, 10] to draw a dot-dash line, and so on. + this.linePattern = 'solid'; + // prop: lineJoin + // How line segments of the shadow are joined. + this.lineJoin = 'miter'; + // prop: lineCap + // how ends of the shadow line are rendered. + this.lineCap = 'round'; + // prop; closePath + // whether line path segment is closed upon itself. + this.closePath = false; + // prop: fill + // whether to fill the shape. + this.fill = false; + // prop: isarc + // whether the shadow is an arc or not. + this.isarc = false; + // prop: fillRect + // true to draw shape as a filled rectangle. + this.fillRect = false; + // prop: strokeRect + // true to draw shape as a stroked rectangle. + this.strokeRect = false; + // prop: clearRect + // true to cear a rectangle. + this.clearRect = false; + // prop: strokeStyle + // css color spec for the stoke style + this.strokeStyle = '#999999'; + // prop: fillStyle + // css color spec for the fill style. + this.fillStyle = '#999999'; + + $.extend(true, this, options); + }; + + $.jqplot.ShapeRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + // function: draw + // draws the shape. + // + // ctx - canvas drawing context + // points - array of points for shapes or + // [x, y, width, height] for rectangles or + // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs. + $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) { + ctx.save(); + var opts = (options != null) ? options : {}; + var fill = (opts.fill != null) ? opts.fill : this.fill; + var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; + var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; + var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect; + var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect; + var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; + var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; + var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); + ctx.lineWidth = opts.lineWidth || this.lineWidth; + ctx.lineJoin = opts.lineJoin || this.lineJoin; + ctx.lineCap = opts.lineCap || this.lineCap; + ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle; + ctx.fillStyle = opts.fillStyle || this.fillStyle; + ctx.beginPath(); + if (isarc) { + ctx.arc(points[0], points[1], points[2], points[3], points[4], true); + if (closePath) { + ctx.closePath(); + } + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + ctx.restore(); + return; + } + else if (clearRect) { + ctx.clearRect(points[0], points[1], points[2], points[3]); + ctx.restore(); + return; + } + else if (fillRect || strokeRect) { + if (fillRect) { + ctx.fillRect(points[0], points[1], points[2], points[3]); + } + if (strokeRect) { + ctx.strokeRect(points[0], points[1], points[2], points[3]); + ctx.restore(); + return; + } + } + else if (points && points.length){ + var move = true; + for (var i=0; i<points.length; i++) { + // skip to the first non-null point and move to it. + if (points[i][0] != null && points[i][1] != null) { + if (move) { + ctxPattern.moveTo(points[i][0], points[i][1]); + move = false; + } + else { + ctxPattern.lineTo(points[i][0], points[i][1]); + } + } + else { + move = true; + } + } + if (closePath) { + ctxPattern.closePath(); + } + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + } + ctx.restore(); + }; + + // class $.jqplot.TableLegendRenderer + // The default legend renderer for jqPlot. + $.jqplot.TableLegendRenderer = function(){ + // + }; + + $.jqplot.TableLegendRenderer.prototype.init = function(options) { + $.extend(true, this, options); + }; + + $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) { + var rs = (pad) ? this.rowSpacing+'px' : '0px'; + var tr; + var td; + var elem; + var div0; + var div1; + elem = document.createElement('tr'); + tr = $(elem); + tr.addClass('jqplot-table-legend'); + elem = null; + + if (reverse){ + tr.prependTo(this._elem); + } + + else{ + tr.appendTo(this._elem); + } + + if (this.showSwatches) { + td = $(document.createElement('td')); + td.addClass('jqplot-table-legend jqplot-table-legend-swatch'); + td.css({textAlign: 'center', paddingTop: rs}); + + div0 = $(document.createElement('div')); + div0.addClass('jqplot-table-legend-swatch-outline'); + div1 = $(document.createElement('div')); + div1.addClass('jqplot-table-legend-swatch'); + div1.css({backgroundColor: color, borderColor: color}); + + tr.append(td.append(div0.append(div1))); + + // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ + // '</div></td>').appendTo(tr); + } + if (this.showLabels) { + td = $(document.createElement('td')); + td.addClass('jqplot-table-legend jqplot-table-legend-label'); + td.css('paddingTop', rs); + tr.append(td); + + // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + // elem.appendTo(tr); + if (this.escapeHtml) { + td.text(label); + } + else { + td.html(label); + } + } + td = null; + div0 = null; + div1 = null; + tr = null; + elem = null; + }; + + // called with scope of legend + $.jqplot.TableLegendRenderer.prototype.draw = function() { + if (this._elem) { + this._elem.emptyForce(); + this._elem = null; + } + + if (this.show) { + var series = this._series; + // make a table. one line label per row. + var elem = document.createElement('table'); + this._elem = $(elem); + this._elem.addClass('jqplot-table-legend'); + + var ss = {position:'absolute'}; + if (this.background) { + ss['background'] = this.background; + } + if (this.border) { + ss['border'] = this.border; + } + if (this.fontSize) { + ss['fontSize'] = this.fontSize; + } + if (this.fontFamily) { + ss['fontFamily'] = this.fontFamily; + } + if (this.textColor) { + ss['textColor'] = this.textColor; + } + if (this.marginTop != null) { + ss['marginTop'] = this.marginTop; + } + if (this.marginBottom != null) { + ss['marginBottom'] = this.marginBottom; + } + if (this.marginLeft != null) { + ss['marginLeft'] = this.marginLeft; + } + if (this.marginRight != null) { + ss['marginRight'] = this.marginRight; + } + + + var pad = false, + reverse = false, + s; + for (var i = 0; i< series.length; i++) { + s = series[i]; + if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){ + reverse = true; + } + if (s.show && s.showLabel) { + var lt = this.labels[i] || s.label.toString(); + if (lt) { + var color = s.color; + if (reverse && i < series.length - 1){ + pad = true; + } + else if (reverse && i == series.length - 1){ + pad = false; + } + this.renderer.addrow.call(this, lt, color, pad, reverse); + pad = true; + } + // let plugins add more rows to legend. Used by trend line plugin. + for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { + var item = $.jqplot.addLegendRowHooks[j].call(this, s); + if (item) { + this.renderer.addrow.call(this, item.label, item.color, pad); + pad = true; + } + } + lt = null; + } + } + } + return this._elem; + }; + + $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) { + if (this.show) { + if (this.placement == 'insideGrid') { + switch (this.location) { + case 'nw': + var a = offsets.left; + var b = offsets.top; + this._elem.css('left', a); + this._elem.css('top', b); + break; + case 'n': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = offsets.top; + this._elem.css('left', a); + this._elem.css('top', b); + break; + case 'ne': + var a = offsets.right; + var b = offsets.top; + this._elem.css({right:a, top:b}); + break; + case 'e': + var a = offsets.right; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({right:a, top:b}); + break; + case 'se': + var a = offsets.right; + var b = offsets.bottom; + this._elem.css({right:a, bottom:b}); + break; + case 's': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = offsets.bottom; + this._elem.css({left:a, bottom:b}); + break; + case 'sw': + var a = offsets.left; + var b = offsets.bottom; + this._elem.css({left:a, bottom:b}); + break; + case 'w': + var a = offsets.left; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({left:a, top:b}); + break; + default: // same as 'se' + var a = offsets.right; + var b = offsets.bottom; + this._elem.css({right:a, bottom:b}); + break; + } + + } + else if (this.placement == 'outside'){ + switch (this.location) { + case 'nw': + var a = this._plotDimensions.width - offsets.left; + var b = offsets.top; + this._elem.css('right', a); + this._elem.css('top', b); + break; + case 'n': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = this._plotDimensions.height - offsets.top; + this._elem.css('left', a); + this._elem.css('bottom', b); + break; + case 'ne': + var a = this._plotDimensions.width - offsets.right; + var b = offsets.top; + this._elem.css({left:a, top:b}); + break; + case 'e': + var a = this._plotDimensions.width - offsets.right; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({left:a, top:b}); + break; + case 'se': + var a = this._plotDimensions.width - offsets.right; + var b = offsets.bottom; + this._elem.css({left:a, bottom:b}); + break; + case 's': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = this._plotDimensions.height - offsets.bottom; + this._elem.css({left:a, top:b}); + break; + case 'sw': + var a = this._plotDimensions.width - offsets.left; + var b = offsets.bottom; + this._elem.css({right:a, bottom:b}); + break; + case 'w': + var a = this._plotDimensions.width - offsets.left; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({right:a, top:b}); + break; + default: // same as 'se' + var a = offsets.right; + var b = offsets.bottom; + this._elem.css({right:a, bottom:b}); + break; + } + } + else { + switch (this.location) { + case 'nw': + this._elem.css({left:0, top:offsets.top}); + break; + case 'n': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + this._elem.css({left: a, top:offsets.top}); + break; + case 'ne': + this._elem.css({right:0, top:offsets.top}); + break; + case 'e': + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({right:offsets.right, top:b}); + break; + case 'se': + this._elem.css({right:offsets.right, bottom:offsets.bottom}); + break; + case 's': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + this._elem.css({left: a, bottom:offsets.bottom}); + break; + case 'sw': + this._elem.css({left:offsets.left, bottom:offsets.bottom}); + break; + case 'w': + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({left:offsets.left, top:b}); + break; + default: // same as 'se' + this._elem.css({right:offsets.right, bottom:offsets.bottom}); + break; + } + } + } + }; + + /** + * Class: $.jqplot.ThemeEngine + * Theme Engine provides a programatic way to change some of the more + * common jqplot styling options such as fonts, colors and grid options. + * A theme engine instance is created with each plot. The theme engine + * manages a collection of themes which can be modified, added to, or + * applied to the plot. + * + * The themeEngine class is not instantiated directly. + * When a plot is initialized, the current plot options are scanned + * an a default theme named "Default" is created. This theme is + * used as the basis for other themes added to the theme engine and + * is always available. + * + * A theme is a simple javascript object with styling parameters for + * various entities of the plot. A theme has the form: + * + * + * > { + * > _name:f "Default", + * > target: { + * > backgroundColor: "transparent" + * > }, + * > legend: { + * > textColor: null, + * > fontFamily: null, + * > fontSize: null, + * > border: null, + * > background: null + * > }, + * > title: { + * > textColor: "rgb(102, 102, 102)", + * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", + * > fontSize: "19.2px", + * > textAlign: "center" + * > }, + * > seriesStyles: {}, + * > series: [{ + * > color: "#4bb2c5", + * > lineWidth: 2.5, + * > linePattern: "solid", + * > shadow: true, + * > fillColor: "#4bb2c5", + * > showMarker: true, + * > markerOptions: { + * > color: "#4bb2c5", + * > show: true, + * > style: 'filledCircle', + * > lineWidth: 1.5, + * > size: 4, + * > shadow: true + * > } + * > }], + * > grid: { + * > drawGridlines: true, + * > gridLineColor: "#cccccc", + * > gridLineWidth: 1, + * > backgroundColor: "#fffdf6", + * > borderColor: "#999999", + * > borderWidth: 2, + * > shadow: true + * > }, + * > axesStyles: { + * > label: {}, + * > ticks: {} + * > }, + * > axes: { + * > xaxis: { + * > borderColor: "#999999", + * > borderWidth: 2, + * > ticks: { + * > show: true, + * > showGridline: true, + * > showLabel: true, + * > showMark: true, + * > size: 4, + * > textColor: "", + * > whiteSpace: "nowrap", + * > fontSize: "12px", + * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" + * > }, + * > label: { + * > textColor: "rgb(102, 102, 102)", + * > whiteSpace: "normal", + * > fontSize: "14.6667px", + * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", + * > fontWeight: "400" + * > } + * > }, + * > yaxis: { + * > borderColor: "#999999", + * > borderWidth: 2, + * > ticks: { + * > show: true, + * > showGridline: true, + * > showLabel: true, + * > showMark: true, + * > size: 4, + * > textColor: "", + * > whiteSpace: "nowrap", + * > fontSize: "12px", + * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" + * > }, + * > label: { + * > textColor: null, + * > whiteSpace: null, + * > fontSize: null, + * > fontFamily: null, + * > fontWeight: null + * > } + * > }, + * > x2axis: {... + * > }, + * > ... + * > y9axis: {... + * > } + * > } + * > } + * + * "seriesStyles" is a style object that will be applied to all series in the plot. + * It will forcibly override any styles applied on the individual series. "axesStyles" is + * a style object that will be applied to all axes in the plot. It will also forcibly + * override any styles on the individual axes. + * + * The example shown above has series options for a line series. Options for other + * series types are shown below: + * + * Bar Series: + * + * > { + * > color: "#4bb2c5", + * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], + * > lineWidth: 2.5, + * > shadow: true, + * > barPadding: 2, + * > barMargin: 10, + * > barWidth: 15.09375, + * > highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"] + * > } + * + * Pie Series: + * + * > { + * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], + * > padding: 20, + * > sliceMargin: 0, + * > fill: true, + * > shadow: true, + * > startAngle: 0, + * > lineWidth: 2.5, + * > highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"] + * > } + * + * Funnel Series: + * + * > { + * > color: "#4bb2c5", + * > lineWidth: 2, + * > shadow: true, + * > padding: { + * > top: 20, + * > right: 20, + * > bottom: 20, + * > left: 20 + * > }, + * > sectionMargin: 6, + * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], + * > highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"] + * > } + * + */ + $.jqplot.ThemeEngine = function(){ + // Group: Properties + // + // prop: themes + // hash of themes managed by the theme engine. + // Indexed by theme name. + this.themes = {}; + // prop: activeTheme + // Pointer to currently active theme + this.activeTheme=null; + + }; + + // called with scope of plot + $.jqplot.ThemeEngine.prototype.init = function() { + // get the Default theme from the current plot settings. + var th = new $.jqplot.Theme({_name:'Default'}); + var n, i, nn; + + for (n in th.target) { + if (n == "textColor") { + th.target[n] = this.target.css('color'); + } + else { + th.target[n] = this.target.css(n); + } + } + + if (this.title.show && this.title._elem) { + for (n in th.title) { + if (n == "textColor") { + th.title[n] = this.title._elem.css('color'); + } + else { + th.title[n] = this.title._elem.css(n); + } + } + } + + for (n in th.grid) { + th.grid[n] = this.grid[n]; + } + if (th.grid.backgroundColor == null && this.grid.background != null) { + th.grid.backgroundColor = this.grid.background; + } + if (this.legend.show && this.legend._elem) { + for (n in th.legend) { + if (n == 'textColor') { + th.legend[n] = this.legend._elem.css('color'); + } + else { + th.legend[n] = this.legend._elem.css(n); + } + } + } + var s; + + for (i=0; i<this.series.length; i++) { + s = this.series[i]; + if (s.renderer.constructor == $.jqplot.LineRenderer) { + th.series.push(new LineSeriesProperties()); + } + else if (s.renderer.constructor == $.jqplot.BarRenderer) { + th.series.push(new BarSeriesProperties()); + } + else if (s.renderer.constructor == $.jqplot.PieRenderer) { + th.series.push(new PieSeriesProperties()); + } + else if (s.renderer.constructor == $.jqplot.DonutRenderer) { + th.series.push(new DonutSeriesProperties()); + } + else if (s.renderer.constructor == $.jqplot.FunnelRenderer) { + th.series.push(new FunnelSeriesProperties()); + } + else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) { + th.series.push(new MeterSeriesProperties()); + } + else { + th.series.push({}); + } + for (n in th.series[i]) { + th.series[i][n] = s[n]; + } + } + var a, ax; + for (n in this.axes) { + ax = this.axes[n]; + a = th.axes[n] = new AxisProperties(); + a.borderColor = ax.borderColor; + a.borderWidth = ax.borderWidth; + if (ax._ticks && ax._ticks[0]) { + for (nn in a.ticks) { + if (ax._ticks[0].hasOwnProperty(nn)) { + a.ticks[nn] = ax._ticks[0][nn]; + } + else if (ax._ticks[0]._elem){ + a.ticks[nn] = ax._ticks[0]._elem.css(nn); + } + } + } + if (ax._label && ax._label.show) { + for (nn in a.label) { + // a.label[nn] = ax._label._elem.css(nn); + if (ax._label[nn]) { + a.label[nn] = ax._label[nn]; + } + else if (ax._label._elem){ + if (nn == 'textColor') { + a.label[nn] = ax._label._elem.css('color'); + } + else { + a.label[nn] = ax._label._elem.css(nn); + } + } + } + } + } + this.themeEngine._add(th); + this.themeEngine.activeTheme = this.themeEngine.themes[th._name]; + }; + /** + * Group: methods + * + * method: get + * + * Get and return the named theme or the active theme if no name given. + * + * parameter: + * + * name - name of theme to get. + * + * returns: + * + * Theme instance of given name. + */ + $.jqplot.ThemeEngine.prototype.get = function(name) { + if (!name) { + // return the active theme + return this.activeTheme; + } + else { + return this.themes[name]; + } + }; + + function numericalOrder(a,b) { return a-b; } + + /** + * method: getThemeNames + * + * Return the list of theme names in this manager in alpha-numerical order. + * + * parameter: + * + * None + * + * returns: + * + * A the list of theme names in this manager in alpha-numerical order. + */ + $.jqplot.ThemeEngine.prototype.getThemeNames = function() { + var tn = []; + for (var n in this.themes) { + tn.push(n); + } + return tn.sort(numericalOrder); + }; + + /** + * method: getThemes + * + * Return a list of themes in alpha-numerical order by name. + * + * parameter: + * + * None + * + * returns: + * + * A list of themes in alpha-numerical order by name. + */ + $.jqplot.ThemeEngine.prototype.getThemes = function() { + var tn = []; + var themes = []; + for (var n in this.themes) { + tn.push(n); + } + tn.sort(numericalOrder); + for (var i=0; i<tn.length; i++) { + themes.push(this.themes[tn[i]]); + } + return themes; + }; + + $.jqplot.ThemeEngine.prototype.activate = function(plot, name) { + // sometimes need to redraw whole plot. + var redrawPlot = false; + if (!name && this.activeTheme && this.activeTheme._name) { + name = this.activeTheme._name; + } + if (!this.themes.hasOwnProperty(name)) { + throw new Error("No theme of that name"); + } + else { + var th = this.themes[name]; + this.activeTheme = th; + var val, checkBorderColor = false, checkBorderWidth = false; + var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis']; + + for (i=0; i<arr.length; i++) { + var ax = arr[i]; + if (th.axesStyles.borderColor != null) { + plot.axes[ax].borderColor = th.axesStyles.borderColor; + } + if (th.axesStyles.borderWidth != null) { + plot.axes[ax].borderWidth = th.axesStyles.borderWidth; + } + } + + for (var axname in plot.axes) { + var axis = plot.axes[axname]; + if (axis.show) { + var thaxis = th.axes[axname] || {}; + var thaxstyle = th.axesStyles; + var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle); + val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor; + if (thax.borderColor != null) { + axis.borderColor = thax.borderColor; + redrawPlot = true; + } + val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth; + if (thax.borderWidth != null) { + axis.borderWidth = thax.borderWidth; + redrawPlot = true; + } + if (axis._ticks && axis._ticks[0]) { + for (var nn in thax.ticks) { + // val = null; + // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) { + // val = th.axesStyles.ticks[nn]; + // } + // else if (thax.ticks[nn] != null){ + // val = thax.ticks[nn] + // } + val = thax.ticks[nn]; + if (val != null) { + axis.tickOptions[nn] = val; + axis._ticks = []; + redrawPlot = true; + } + } + } + if (axis._label && axis._label.show) { + for (var nn in thax.label) { + // val = null; + // if (th.axesStyles.label && th.axesStyles.label[nn] != null) { + // val = th.axesStyles.label[nn]; + // } + // else if (thax.label && thax.label[nn] != null){ + // val = thax.label[nn] + // } + val = thax.label[nn]; + if (val != null) { + axis.labelOptions[nn] = val; + redrawPlot = true; + } + } + } + + } + } + + for (var n in th.grid) { + if (th.grid[n] != null) { + plot.grid[n] = th.grid[n]; + } + } + if (!redrawPlot) { + plot.grid.draw(); + } + + if (plot.legend.show) { + for (n in th.legend) { + if (th.legend[n] != null) { + plot.legend[n] = th.legend[n]; + } + } + } + if (plot.title.show) { + for (n in th.title) { + if (th.title[n] != null) { + plot.title[n] = th.title[n]; + } + } + } + + var i; + for (i=0; i<th.series.length; i++) { + var opts = {}; + var redrawSeries = false; + for (n in th.series[i]) { + val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n]; + if (val != null) { + opts[n] = val; + if (n == 'color') { + plot.series[i].renderer.shapeRenderer.fillStyle = val; + plot.series[i].renderer.shapeRenderer.strokeStyle = val; + plot.series[i][n] = val; + } + else if ((n == 'lineWidth') || (n == 'linePattern')) { + plot.series[i].renderer.shapeRenderer[n] = val; + plot.series[i][n] = val; + } + else if (n == 'markerOptions') { + merge (plot.series[i].markerOptions, val); + merge (plot.series[i].markerRenderer, val); + } + else { + plot.series[i][n] = val; + } + redrawPlot = true; + } + } + } + + if (redrawPlot) { + plot.target.empty(); + plot.draw(); + } + + for (n in th.target) { + if (th.target[n] != null) { + plot.target.css(n, th.target[n]); + } + } + } + + }; + + $.jqplot.ThemeEngine.prototype._add = function(theme, name) { + if (name) { + theme._name = name; + } + if (!theme._name) { + theme._name = Date.parse(new Date()); + } + if (!this.themes.hasOwnProperty(theme._name)) { + this.themes[theme._name] = theme; + } + else { + throw new Error("jqplot.ThemeEngine Error: Theme already in use"); + } + }; + + // method remove + // Delete the named theme, return true on success, false on failure. + + + /** + * method: remove + * + * Remove the given theme from the themeEngine. + * + * parameters: + * + * name - name of the theme to remove. + * + * returns: + * + * true on success, false on failure. + */ + $.jqplot.ThemeEngine.prototype.remove = function(name) { + if (name == 'Default') { + return false; + } + return delete this.themes[name]; + }; + + /** + * method: newTheme + * + * Create a new theme based on the default theme, adding it the themeEngine. + * + * parameters: + * + * name - name of the new theme. + * obj - optional object of styles to be applied to this new theme. + * + * returns: + * + * new Theme object. + */ + $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) { + if (typeof(name) == 'object') { + obj = obj || name; + name = null; + } + if (obj && obj._name) { + name = obj._name; + } + else { + name = name || Date.parse(new Date()); + } + // var th = new $.jqplot.Theme(name); + var th = this.copy(this.themes['Default']._name, name); + $.jqplot.extend(th, obj); + return th; + }; + + // function clone(obj) { + // return eval(obj.toSource()); + // } + + function clone(obj){ + if(obj == null || typeof(obj) != 'object'){ + return obj; + } + + var temp = new obj.constructor(); + for(var key in obj){ + temp[key] = clone(obj[key]); + } + return temp; + } + + $.jqplot.clone = clone; + + function merge(obj1, obj2) { + if (obj2 == null || typeof(obj2) != 'object') { + return; + } + for (var key in obj2) { + if (key == 'highlightColors') { + obj1[key] = clone(obj2[key]); + } + if (obj2[key] != null && typeof(obj2[key]) == 'object') { + if (!obj1.hasOwnProperty(key)) { + obj1[key] = {}; + } + merge(obj1[key], obj2[key]); + } + else { + obj1[key] = obj2[key]; + } + } + } + + $.jqplot.merge = merge; + + // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic + $.jqplot.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) { + target = {}; + } + + for ( ; i < length; i++ ){ + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) { + target[ name ] = $.jqplot.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + } + // Don't bring in undefined values + else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + // Return the modified object + return target; + }; + + /** + * method: rename + * + * Rename a theme. + * + * parameters: + * + * oldName - current name of the theme. + * newName - desired name of the theme. + * + * returns: + * + * new Theme object. + */ + $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) { + if (oldName == 'Default' || newName == 'Default') { + throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default"); + } + if (this.themes.hasOwnProperty(newName)) { + throw new Error ("jqplot.ThemeEngine Error: New name already in use."); + } + else if (this.themes.hasOwnProperty(oldName)) { + var th = this.copy (oldName, newName); + this.remove(oldName); + return th; + } + throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid"); + }; + + /** + * method: copy + * + * Create a copy of an existing theme in the themeEngine, adding it the themeEngine. + * + * parameters: + * + * sourceName - name of the existing theme. + * targetName - name of the copy. + * obj - optional object of style parameter to apply to the new theme. + * + * returns: + * + * new Theme object. + */ + $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) { + if (targetName == 'Default') { + throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme"); + } + if (!this.themes.hasOwnProperty(sourceName)) { + var s = "jqplot.ThemeEngine Error: Source name invalid"; + throw new Error(s); + } + if (this.themes.hasOwnProperty(targetName)) { + var s = "jqplot.ThemeEngine Error: Target name invalid"; + throw new Error(s); + } + else { + var th = clone(this.themes[sourceName]); + th._name = targetName; + $.jqplot.extend(true, th, obj); + this._add(th); + return th; + } + }; + + + $.jqplot.Theme = function(name, obj) { + if (typeof(name) == 'object') { + obj = obj || name; + name = null; + } + name = name || Date.parse(new Date()); + this._name = name; + this.target = { + backgroundColor: null + }; + this.legend = { + textColor: null, + fontFamily: null, + fontSize: null, + border: null, + background: null + }; + this.title = { + textColor: null, + fontFamily: null, + fontSize: null, + textAlign: null + }; + this.seriesStyles = {}; + this.series = []; + this.grid = { + drawGridlines: null, + gridLineColor: null, + gridLineWidth: null, + backgroundColor: null, + borderColor: null, + borderWidth: null, + shadow: null + }; + this.axesStyles = {label:{}, ticks:{}}; + this.axes = {}; + if (typeof(obj) == 'string') { + this._name = obj; + } + else if(typeof(obj) == 'object') { + $.jqplot.extend(true, this, obj); + } + }; + + var AxisProperties = function() { + this.borderColor = null; + this.borderWidth = null; + this.ticks = new AxisTicks(); + this.label = new AxisLabel(); + }; + + var AxisTicks = function() { + this.show = null; + this.showGridline = null; + this.showLabel = null; + this.showMark = null; + this.size = null; + this.textColor = null; + this.whiteSpace = null; + this.fontSize = null; + this.fontFamily = null; + }; + + var AxisLabel = function() { + this.textColor = null; + this.whiteSpace = null; + this.fontSize = null; + this.fontFamily = null; + this.fontWeight = null; + }; + + var LineSeriesProperties = function() { + this.color=null; + this.lineWidth=null; + this.linePattern=null; + this.shadow=null; + this.fillColor=null; + this.showMarker=null; + this.markerOptions = new MarkerOptions(); + }; + + var MarkerOptions = function() { + this.show = null; + this.style = null; + this.lineWidth = null; + this.size = null; + this.color = null; + this.shadow = null; + }; + + var BarSeriesProperties = function() { + this.color=null; + this.seriesColors=null; + this.lineWidth=null; + this.shadow=null; + this.barPadding=null; + this.barMargin=null; + this.barWidth=null; + this.highlightColors=null; + }; + + var PieSeriesProperties = function() { + this.seriesColors=null; + this.padding=null; + this.sliceMargin=null; + this.fill=null; + this.shadow=null; + this.startAngle=null; + this.lineWidth=null; + this.highlightColors=null; + }; + + var DonutSeriesProperties = function() { + this.seriesColors=null; + this.padding=null; + this.sliceMargin=null; + this.fill=null; + this.shadow=null; + this.startAngle=null; + this.lineWidth=null; + this.innerDiameter=null; + this.thickness=null; + this.ringMargin=null; + this.highlightColors=null; + }; + + var FunnelSeriesProperties = function() { + this.color=null; + this.lineWidth=null; + this.shadow=null; + this.padding=null; + this.sectionMargin=null; + this.seriesColors=null; + this.highlightColors=null; + }; + + var MeterSeriesProperties = function() { + this.padding=null; + this.backgroundColor=null; + this.ringColor=null; + this.tickColor=null; + this.ringWidth=null; + this.intervalColors=null; + this.intervalInnerRadius=null; + this.intervalOuterRadius=null; + this.hubRadius=null; + this.needleThickness=null; + this.needlePad=null; + }; + + + + + $.fn.jqplotChildText = function() { + return $(this).contents().filter(function() { + return this.nodeType == 3; // Node.TEXT_NODE not defined in I7 + }).text(); + }; + + // Returns font style as abbreviation for "font" property. + $.fn.jqplotGetComputedFontStyle = function() { + var css = window.getComputedStyle ? window.getComputedStyle(this[0], "") : this[0].currentStyle; + var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; + var style = []; + + for (var i=0 ; i < attrs.length; ++i) { + var attr = String(css[attrs[i]]); + + if (attr && attr != 'normal') { + style.push(attr); + } + } + return style.join(' '); + }; + + /** + * Namespace: $.fn + * jQuery namespace to attach functions to jQuery elements. + * + */ + + $.fn.jqplotToImageCanvas = function(options) { + + options = options || {}; + var x_offset = (options.x_offset == null) ? 0 : options.x_offset; + var y_offset = (options.y_offset == null) ? 0 : options.y_offset; + var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor; + + if ($(this).width() == 0 || $(this).height() == 0) { + return null; + } + + // excanvas and hence IE < 9 do not support toDataURL and cannot export images. + if ($.jqplot.use_excanvas) { + return null; + } + + var newCanvas = document.createElement("canvas"); + var h = $(this).outerHeight(true); + var w = $(this).outerWidth(true); + var offs = $(this).offset(); + var plotleft = offs.left; + var plottop = offs.top; + var transx = 0, transy = 0; + + // have to check if any elements are hanging outside of plot area before rendering, + // since changing width of canvas will erase canvas. + + var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', + 'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick', + 'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', + 'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ]; + + var temptop, templeft, tempbottom, tempright; + + for (var i = 0; i < clses.length; i++) { + $(this).find('.'+clses[i]).each(function() { + temptop = $(this).offset().top - plottop; + templeft = $(this).offset().left - plotleft; + tempright = templeft + $(this).outerWidth(true) + transx; + tempbottom = temptop + $(this).outerHeight(true) + transy; + if (templeft < -transx) { + w = w - transx - templeft; + transx = -templeft; + } + if (temptop < -transy) { + h = h - transy - temptop; + transy = - temptop; + } + if (tempright > w) { + w = tempright; + } + if (tempbottom > h) { + h = tempbottom; + } + }); + } + + newCanvas.width = w + Number(x_offset); + newCanvas.height = h + Number(y_offset); + + var newContext = newCanvas.getContext("2d"); + + newContext.save(); + newContext.fillStyle = backgroundColor; + newContext.fillRect(0,0, newCanvas.width, newCanvas.height); + newContext.restore(); + + newContext.translate(transx, transy); + newContext.textAlign = 'left'; + newContext.textBaseline = 'top'; + + function getLineheight(el) { + var lineheight = parseInt($(el).css('line-height'), 10); + + if (isNaN(lineheight)) { + lineheight = parseInt($(el).css('font-size'), 10) * 1.2; + } + return lineheight; + } + + function writeWrappedText (el, context, text, left, top, canvasWidth) { + var lineheight = getLineheight(el); + var tagwidth = $(el).innerWidth(); + var tagheight = $(el).innerHeight(); + var words = text.split(/\s+/); + var wl = words.length; + var w = ''; + var breaks = []; + var temptop = top; + var templeft = left; + + for (var i=0; i<wl; i++) { + w += words[i]; + if (context.measureText(w).width > tagwidth) { + breaks.push(i); + w = ''; + i--; + } + } + if (breaks.length === 0) { + // center text if necessary + if ($(el).css('textAlign') === 'center') { + templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; + } + context.fillText(text, templeft, top); + } + else { + w = words.slice(0, breaks[0]).join(' '); + // center text if necessary + if ($(el).css('textAlign') === 'center') { + templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; + } + context.fillText(w, templeft, temptop); + temptop += lineheight; + for (var i=1, l=breaks.length; i<l; i++) { + w = words.slice(breaks[i-1], breaks[i]).join(' '); + // center text if necessary + if ($(el).css('textAlign') === 'center') { + templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; + } + context.fillText(w, templeft, temptop); + temptop += lineheight; + } + w = words.slice(breaks[i-1], words.length).join(' '); + // center text if necessary + if ($(el).css('textAlign') === 'center') { + templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; + } + context.fillText(w, templeft, temptop); + } + + } + + function _jqpToImage(el, x_offset, y_offset) { + var tagname = el.tagName.toLowerCase(); + var p = $(el).position(); + var css = window.getComputedStyle ? window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9 + var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10); + var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10); + var w = newCanvas.width; + // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') + + // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas. + + if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) { + $(el).children().each(function() { + _jqpToImage(this, left, top); + }); + var text = $(el).jqplotChildText(); + + if (text) { + newContext.font = $(el).jqplotGetComputedFontStyle(); + newContext.fillStyle = $(el).css('color'); + + writeWrappedText(el, newContext, text, left, top, w); + } + } + + // handle the standard table legend + + else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) { + newContext.strokeStyle = $(el).css('border-top-color'); + newContext.fillStyle = $(el).css('background-color'); + newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight()); + if (parseInt($(el).css('border-top-width'), 10) > 0) { + newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight()); + } + + // find all the swatches + $(el).find('div.jqplot-table-legend-swatch-outline').each(function() { + // get the first div and stroke it + var elem = $(this); + newContext.strokeStyle = elem.css('border-top-color'); + var l = left + elem.position().left; + var t = top + elem.position().top; + newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight()); + + // now fill the swatch + + l += parseInt(elem.css('padding-left'), 10); + t += parseInt(elem.css('padding-top'), 10); + var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10); + var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10); + + var swatch = elem.children('div.jqplot-table-legend-swatch'); + newContext.fillStyle = swatch.css('background-color'); + newContext.fillRect(l, t, w, h); + }); + + // now add text + + $(el).find('td.jqplot-table-legend-label').each(function(){ + var elem = $(this); + var l = left + elem.position().left; + var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10); + newContext.font = elem.jqplotGetComputedFontStyle(); + newContext.fillStyle = elem.css('color'); + writeWrappedText(elem, newContext, elem.text(), l, t, w); + }); + + var elem = null; + } + + else if (tagname == 'canvas') { + newContext.drawImage(el, left, top); + } + } + $(this).children().each(function() { + _jqpToImage(this, x_offset, y_offset); + }); + return newCanvas; + }; + + // return the raw image data string. + // Should work on canvas supporting browsers. + $.fn.jqplotToImageStr = function(options) { + var imgCanvas = $(this).jqplotToImageCanvas(options); + if (imgCanvas) { + return imgCanvas.toDataURL("image/png"); + } + else { + return null; + } + }; + + // return a DOM <img> element and return it. + // Should work on canvas supporting browsers. + $.fn.jqplotToImageElem = function(options) { + var elem = document.createElement("img"); + var str = $(this).jqplotToImageStr(options); + elem.src = str; + return elem; + }; + + // return a string for an <img> element and return it. + // Should work on canvas supporting browsers. + $.fn.jqplotToImageElemStr = function(options) { + var str = '<img src='+$(this).jqplotToImageStr(options)+' />'; + return str; + }; + + // Not guaranteed to work, even on canvas supporting browsers due to + // limitations with location.href and browser support. + $.fn.jqplotSaveImage = function() { + var imgData = $(this).jqplotToImageStr({}); + if (imgData) { + window.location.href = imgData.replace("image/png", "image/octet-stream"); + } + + }; + + // Not guaranteed to work, even on canvas supporting browsers due to + // limitations with window.open and arbitrary data. + $.fn.jqplotViewImage = function() { + var imgStr = $(this).jqplotToImageElemStr({}); + var imgData = $(this).jqplotToImageStr({}); + if (imgStr) { + var w = window.open(''); + w.document.open("image/png"); + w.document.write(imgStr); + w.document.close(); + w = null; + } + }; + + + + + /** + * @description + * <p>Object with extended date parsing and formatting capabilities. + * This library borrows many concepts and ideas from the Date Instance + * Methods by Ken Snyder along with some parts of Ken's actual code.</p> + * + * <p>jsDate takes a different approach by not extending the built-in + * Date Object, improving date parsing, allowing for multiple formatting + * syntaxes and multiple and more easily expandable localization.</p> + * + * @author Chris Leonello + * @date #date# + * @version #VERSION# + * @copyright (c) 2010-2013 Chris Leonello + * jsDate is currently available for use in all personal or commercial projects + * under both the MIT and GPL version 2.0 licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * <p>Ken's original Date Instance Methods and copyright notice:</p> + * <pre> + * Ken Snyder (ken d snyder at gmail dot com) + * 2008-09-10 + * version 2.0.2 (http://kendsnyder.com/sandbox/date/) + * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) + * </pre> + * + * @class + * @name jsDate + * @param {String | Number | Array | Date Object | Options Object} arguments Optional arguments, either a parsable date/time string, + * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], + * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional. + */ + + var jsDate = function () { + + this.syntax = jsDate.config.syntax; + this._type = "jsDate"; + this.proxy = new Date(); + this.options = {}; + this.locale = jsDate.regional.getLocale(); + this.formatString = ''; + this.defaultCentury = jsDate.config.defaultCentury; + + switch ( arguments.length ) { + case 0: + break; + case 1: + // other objects either won't have a _type property or, + // if they do, it shouldn't be set to "jsDate", so + // assume it is an options argument. + if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { + var opts = this.options = arguments[0]; + this.syntax = opts.syntax || this.syntax; + this.defaultCentury = opts.defaultCentury || this.defaultCentury; + this.proxy = jsDate.createDate(opts.date); + } + else { + this.proxy = jsDate.createDate(arguments[0]); + } + break; + default: + var a = []; + for ( var i=0; i<arguments.length; i++ ) { + a.push(arguments[i]); + } + // this should be the current date/time? + this.proxy = new Date(); + this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); + if ( a.slice(3).length ) { + this.proxy.setHours.apply( this.proxy, a.slice(3) ); + } + break; + } + }; + + /** + * @namespace Configuration options that will be used as defaults for all instances on the page. + * @property {String} defaultLocale The default locale to use [en]. + * @property {String} syntax The default syntax to use [perl]. + * @property {Number} defaultCentury The default centry for 2 digit dates. + */ + jsDate.config = { + defaultLocale: 'en', + syntax: 'perl', + defaultCentury: 1900 + }; + + /** + * Add an arbitrary amount to the currently stored date + * + * @param {Number} number + * @param {String} unit + * @returns {jsDate} + */ + + jsDate.prototype.add = function(number, unit) { + var factor = multipliers[unit] || multipliers.day; + if (typeof factor == 'number') { + this.proxy.setTime(this.proxy.getTime() + (factor * number)); + } else { + factor.add(this, number); + } + return this; + }; + + /** + * Create a new jqplot.date object with the same date + * + * @returns {jsDate} + */ + + jsDate.prototype.clone = function() { + return new jsDate(this.proxy.getTime()); + }; + + /** + * Get the UTC TimeZone Offset of this date in milliseconds. + * + * @returns {Number} + */ + + jsDate.prototype.getUtcOffset = function() { + return this.proxy.getTimezoneOffset() * 60000; + }; + + /** + * Find the difference between this jsDate and another date. + * + * @param {String| Number| Array| jsDate Object| Date Object} dateObj + * @param {String} unit + * @param {Boolean} allowDecimal + * @returns {Number} Number of units difference between dates. + */ + + jsDate.prototype.diff = function(dateObj, unit, allowDecimal) { + // ensure we have a Date object + dateObj = new jsDate(dateObj); + if (dateObj === null) { + return null; + } + // get the multiplying factor integer or factor function + var factor = multipliers[unit] || multipliers.day; + if (typeof factor == 'number') { + // multiply + var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor; + } else { + // run function + var unitDiff = factor.diff(this.proxy, dateObj.proxy); + } + // if decimals are not allowed, round toward zero + return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff)); + }; + + /** + * Get the abbreviated name of the current week day + * + * @returns {String} + */ + + jsDate.prototype.getAbbrDayName = function() { + return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]; + }; + + /** + * Get the abbreviated name of the current month + * + * @returns {String} + */ + + jsDate.prototype.getAbbrMonthName = function() { + return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]; + }; + + /** + * Get UPPER CASE AM or PM for the current time + * + * @returns {String} + */ + + jsDate.prototype.getAMPM = function() { + return this.proxy.getHours() >= 12 ? 'PM' : 'AM'; + }; + + /** + * Get lower case am or pm for the current time + * + * @returns {String} + */ + + jsDate.prototype.getAmPm = function() { + return this.proxy.getHours() >= 12 ? 'pm' : 'am'; + }; + + /** + * Get the century (19 for 20th Century) + * + * @returns {Integer} Century (19 for 20th century). + */ + jsDate.prototype.getCentury = function() { + return parseInt(this.proxy.getFullYear()/100, 10); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getDate = function() { + return this.proxy.getDate(); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getDay = function() { + return this.proxy.getDay(); + }; + + /** + * Get the Day of week 1 (Monday) thru 7 (Sunday) + * + * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday) + */ + jsDate.prototype.getDayOfWeek = function() { + var dow = this.proxy.getDay(); + return dow===0?7:dow; + }; + + /** + * Get the day of the year + * + * @returns {Integer} 1 - 366, day of the year + */ + jsDate.prototype.getDayOfYear = function() { + var d = this.proxy; + var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT'); + ms += d.getTimezoneOffset()*60000; + d = null; + return parseInt(ms/60000/60/24, 10)+1; + }; + + /** + * Get the name of the current week day + * + * @returns {String} + */ + + jsDate.prototype.getDayName = function() { + return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()]; + }; + + /** + * Get the week number of the given year, starting with the first Sunday as the first week + * @returns {Integer} Week number (13 for the 13th full week of the year). + */ + jsDate.prototype.getFullWeekOfYear = function() { + var d = this.proxy; + var doy = this.getDayOfYear(); + var rdow = 6-d.getDay(); + var woy = parseInt((doy+rdow)/7, 10); + return woy; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getFullYear = function() { + return this.proxy.getFullYear(); + }; + + /** + * Get the GMT offset in hours and minutes (e.g. +06:30) + * + * @returns {String} + */ + + jsDate.prototype.getGmtOffset = function() { + // divide the minutes offset by 60 + var hours = this.proxy.getTimezoneOffset() / 60; + // decide if we are ahead of or behind GMT + var prefix = hours < 0 ? '+' : '-'; + // remove the negative sign if any + hours = Math.abs(hours); + // add the +/- to the padded number of hours to : to the padded minutes + return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getHours = function() { + return this.proxy.getHours(); + }; + + /** + * Get the current hour on a 12-hour scheme + * + * @returns {Integer} + */ + + jsDate.prototype.getHours12 = function() { + var hours = this.proxy.getHours(); + return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); + }; + + + jsDate.prototype.getIsoWeek = function() { + var d = this.proxy; + var woy = this.getWeekOfYear(); + var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); + // First week is 01 and not 00 as in the case of %U and %W, + // so we add 1 to the final result except if day 1 of the year + // is a Monday (then %W returns 01). + // We also need to subtract 1 if the day 1 of the year is + // Friday-Sunday, so the resulting equation becomes: + var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); + if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) + { + idow = 1; + } + else if(idow === 0) + { + d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31')); + idow = d.getIsoWeek(); + } + d = null; + return idow; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getMilliseconds = function() { + return this.proxy.getMilliseconds(); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getMinutes = function() { + return this.proxy.getMinutes(); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getMonth = function() { + return this.proxy.getMonth(); + }; + + /** + * Get the name of the current month + * + * @returns {String} + */ + + jsDate.prototype.getMonthName = function() { + return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()]; + }; + + /** + * Get the number of the current month, 1-12 + * + * @returns {Integer} + */ + + jsDate.prototype.getMonthNumber = function() { + return this.proxy.getMonth() + 1; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getSeconds = function() { + return this.proxy.getSeconds(); + }; + + /** + * Return a proper two-digit year integer + * + * @returns {Integer} + */ + + jsDate.prototype.getShortYear = function() { + return this.proxy.getYear() % 100; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getTime = function() { + return this.proxy.getTime(); + }; + + /** + * Get the timezone abbreviation + * + * @returns {String} Abbreviation for the timezone + */ + jsDate.prototype.getTimezoneAbbr = function() { + return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); + }; + + /** + * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time) + * + * @returns {String} + */ + jsDate.prototype.getTimezoneName = function() { + var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); + return match[1] || match[2] || 'GMT' + this.getGmtOffset(); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getTimezoneOffset = function() { + return this.proxy.getTimezoneOffset(); + }; + + + /** + * Get the week number of the given year, starting with the first Monday as the first week + * @returns {Integer} Week number (13 for the 13th week of the year). + */ + jsDate.prototype.getWeekOfYear = function() { + var doy = this.getDayOfYear(); + var rdow = 7 - this.getDayOfWeek(); + var woy = parseInt((doy+rdow)/7, 10); + return woy; + }; + + /** + * Get the current date as a Unix timestamp + * + * @returns {Integer} + */ + + jsDate.prototype.getUnix = function() { + return Math.round(this.proxy.getTime() / 1000, 0); + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.getYear = function() { + return this.proxy.getYear(); + }; + + /** + * Return a date one day ahead (or any other unit) + * + * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond + * @returns {jsDate} + */ + + jsDate.prototype.next = function(unit) { + unit = unit || 'day'; + return this.clone().add(1, unit); + }; + + /** + * Set the jsDate instance to a new date. + * + * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, + * either a parsable date/time string, + * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], + * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional. + */ + jsDate.prototype.set = function() { + switch ( arguments.length ) { + case 0: + this.proxy = new Date(); + break; + case 1: + // other objects either won't have a _type property or, + // if they do, it shouldn't be set to "jsDate", so + // assume it is an options argument. + if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { + var opts = this.options = arguments[0]; + this.syntax = opts.syntax || this.syntax; + this.defaultCentury = opts.defaultCentury || this.defaultCentury; + this.proxy = jsDate.createDate(opts.date); + } + else { + this.proxy = jsDate.createDate(arguments[0]); + } + break; + default: + var a = []; + for ( var i=0; i<arguments.length; i++ ) { + a.push(arguments[i]); + } + // this should be the current date/time + this.proxy = new Date(); + this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); + if ( a.slice(3).length ) { + this.proxy.setHours.apply( this.proxy, a.slice(3) ); + } + break; + } + return this; + }; + + /** + * Sets the day of the month for a specified date according to local time. + * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. + */ + jsDate.prototype.setDate = function(n) { + this.proxy.setDate(n); + return this; + }; + + /** + * Sets the full year for a specified date according to local time. + * @param {Integer} yearValue The numeric value of the year, for example, 1995. + * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December. + * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue. + */ + jsDate.prototype.setFullYear = function() { + this.proxy.setFullYear.apply(this.proxy, arguments); + return this; + }; + + /** + * Sets the hours for a specified date according to local time. + * + * @param {Integer} hoursValue An integer between 0 and 23, representing the hour. + * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes. + * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. + * If you specify the secondsValue parameter, you must also specify the minutesValue. + * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. + * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. + */ + jsDate.prototype.setHours = function() { + this.proxy.setHours.apply(this.proxy, arguments); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setMilliseconds = function(n) { + this.proxy.setMilliseconds(n); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setMinutes = function() { + this.proxy.setMinutes.apply(this.proxy, arguments); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setMonth = function() { + this.proxy.setMonth.apply(this.proxy, arguments); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setSeconds = function() { + this.proxy.setSeconds.apply(this.proxy, arguments); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setTime = function(n) { + this.proxy.setTime(n); + return this; + }; + + /** + * Implements Date functionality + */ + jsDate.prototype.setYear = function() { + this.proxy.setYear.apply(this.proxy, arguments); + return this; + }; + + /** + * Provide a formatted string representation of this date. + * + * @param {String} formatString A format string. + * See: {@link jsDate.formats}. + * @returns {String} Date String. + */ + + jsDate.prototype.strftime = function(formatString) { + formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString']; + return jsDate.strftime(this, formatString, this.syntax); + }; + + /** + * Return a String representation of this jsDate object. + * @returns {String} Date string. + */ + + jsDate.prototype.toString = function() { + return this.proxy.toString(); + }; + + /** + * Convert the current date to an 8-digit integer (%Y%m%d) + * + * @returns {Integer} + */ + + jsDate.prototype.toYmdInt = function() { + return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate(); + }; + + /** + * @namespace Holds localizations for month/day names. + * <p>jsDate attempts to detect locale when loaded and defaults to 'en'. + * If a localization is detected which is not available, jsDate defaults to 'en'. + * Additional localizations can be added after jsDate loads. After adding a localization, + * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p> + * + * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p> + * <pre class="code"> + * jsDate.regional['en'] = { + * monthNames : 'January February March April May June July August September October November December'.split(' '), + * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), + * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), + * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ') + * }; + * </pre> + * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the + * new localizations.</p> + */ + + jsDate.regional = { + 'en': { + monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'fr': { + monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'], + monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'de': { + monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'es': { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'ru': { + monthNames: ['Январь','Февраль','Март','Ð�прель','Май','Июнь','Июль','Ð�вгуÑ�Ñ‚','СентÑ�брь','ОктÑ�брь','Ð�оÑ�брь','Декабрь'], + monthNamesShort: ['Янв','Фев','Мар','Ð�пр','Май','Июн','Июл','Ð�вг','Сен','Окт','Ð�оÑ�','Дек'], + dayNames: ['воÑ�креÑ�енье','понедельник','вторник','Ñ�реда','четверг','пÑ�тница','Ñ�уббота'], + dayNamesShort: ['вÑ�к','пнд','втр','Ñ�рд','чтв','птн','Ñ�бт'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'ar': { + monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], + monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], + dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'], + dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'pt': { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'pt-BR': { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'pl': { + monthNames: ['StyczeÅ„','Luty','Marzec','KwiecieÅ„','Maj','Czerwiec','Lipiec','SierpieÅ„','WrzesieÅ„','Październik','Listopad','GrudzieÅ„'], + monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], + dayNames: ['Niedziela', 'PoniedziaÅ‚ek', 'Wtorek', 'Åšroda', 'Czwartek', 'PiÄ…tek', 'Sobota'], + dayNamesShort: ['Ni', 'Pn', 'Wt', 'Åšr', 'Cz', 'Pt', 'Sb'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'nl': { + monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'], + dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'], + formatString: '%Y-%m-%d %H:%M:%S' + }, + + 'sv': { + monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'], + monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'], + dayNames: ['söndag','mÃ¥ndag','tisdag','onsdag','torsdag','fredag','lördag'], + dayNamesShort: ['sön','mÃ¥n','tis','ons','tor','fre','lör'], + formatString: '%Y-%m-%d %H:%M:%S' + } + + }; + + // Set english variants to 'en' + jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en']; + + /** + * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en' + * if it cannot figure out a locale of if the locale does not have a localization defined. + * @returns {String} locale + */ + + jsDate.regional.getLocale = function () { + var l = jsDate.config.defaultLocale; + + if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) { + l = document.getElementsByTagName('html')[0].lang; + if (!jsDate.regional.hasOwnProperty(l)) { + l = jsDate.config.defaultLocale; + } + } + + return l; + }; + + // ms in day + var day = 24 * 60 * 60 * 1000; + + // padd a number with zeros + var addZeros = function(num, digits) { + num = String(num); + var i = digits - num.length; + var s = String(Math.pow(10, i)).slice(1); + return s.concat(num); + }; + + // representations used for calculating differences between dates. + // This borrows heavily from Ken Snyder's work. + var multipliers = { + millisecond: 1, + second: 1000, + minute: 60 * 1000, + hour: 60 * 60 * 1000, + day: day, + week: 7 * day, + month: { + // add a number of months + add: function(d, number) { + // add any years needed (increments of 12) + multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); + // ensure that we properly wrap betwen December and January + // 11 % 12 = 11 + // 12 % 12 = 0 + var prevMonth = d.getMonth() + (number % 12); + if (prevMonth == 12) { + prevMonth = 0; + d.setYear(d.getFullYear() + 1); + } else if (prevMonth == -1) { + prevMonth = 11; + d.setYear(d.getFullYear() - 1); + } + d.setMonth(prevMonth); + }, + // get the number of months between two Date objects (decimal to the nearest day) + diff: function(d1, d2) { + // get the number of years + var diffYears = d1.getFullYear() - d2.getFullYear(); + // get the number of remaining months + var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); + // get the number of remaining days + var diffDays = d1.getDate() - d2.getDate(); + // return the month difference with the days difference as a decimal + return diffMonths + (diffDays / 30); + } + }, + year: { + // add a number of years + add: function(d, number) { + d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); + }, + // get the number of years between two Date objects (decimal to the nearest day) + diff: function(d1, d2) { + return multipliers.month.diff(d1, d2) / 12; + } + } + }; + // + // Alias each multiplier with an 's' to allow 'year' and 'years' for example. + // This comes from Ken Snyders work. + // + for (var unit in multipliers) { + if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :| + multipliers[unit + 's'] = multipliers[unit]; + } + } + + // + // take a jsDate instance and a format code and return the formatted value. + // This is a somewhat modified version of Ken Snyder's method. + // + var format = function(d, code, syntax) { + // if shorcut codes are used, recursively expand those. + if (jsDate.formats[syntax]["shortcuts"][code]) { + return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax); + } else { + // get the format code function and addZeros() argument + var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.'); + var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; + if (getter[1]) { + nbr = addZeros(nbr, getter[1]); + } + return nbr; + } + }; + + /** + * @static + * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes. + * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p> + * <pre class="code"> + * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S'); + * </pre> + * @param {String | Number | Array | jsDate Object | Date Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object. + * @param {String} formatString String with embedded date formatting codes. + * See: {@link jsDate.formats}. + * @param {String} syntax Optional syntax to use [default perl]. + * @param {String} locale Optional locale to use. + * @returns {String} Formatted representation of the date. + */ + // + // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods. + // + jsDate.strftime = function(d, formatString, syntax, locale) { + var syn = 'perl'; + var loc = jsDate.regional.getLocale(); + + // check if syntax and locale are available or reversed + if (syntax && jsDate.formats.hasOwnProperty(syntax)) { + syn = syntax; + } + else if (syntax && jsDate.regional.hasOwnProperty(syntax)) { + loc = syntax; + } + + if (locale && jsDate.formats.hasOwnProperty(locale)) { + syn = locale; + } + else if (locale && jsDate.regional.hasOwnProperty(locale)) { + loc = locale; + } + + if (get_type(d) != "[object Object]" || d._type != "jsDate") { + d = new jsDate(d); + d.locale = loc; + } + if (!formatString) { + formatString = d.formatString || jsDate.regional[loc]['formatString']; + } + // default the format string to year-month-day + var source = formatString || '%Y-%m-%d', + result = '', + match; + // replace each format code + while (source.length > 0) { + if (match = source.match(jsDate.formats[syn].codes.matcher)) { + result += source.slice(0, match.index); + result += (match[1] || '') + format(d, match[2], syn); + source = source.slice(match.index + match[0].length); + } else { + result += source; + source = ''; + } + } + return result; + }; + + /** + * @namespace + * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes + * and shortcuts are defined by default. Additional codes and shortcuts can be + * added like: + * + * <pre class="code"> + * jsDate.formats["perl"] = { + * "codes": { + * matcher: /someregex/, + * Y: "fullYear", // name of "get" method without the "get", + * ..., // more codes + * }, + * "shortcuts": { + * F: '%Y-%m-%d', + * ..., // more shortcuts + * } + * }; + * </pre> + * + * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via: + * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code> + */ + + jsDate.formats = { + ISO:'%Y-%m-%dT%H:%M:%S.%N%G', + SQL:'%Y-%m-%d %H:%M:%S' + }; + + /** + * Perl format codes and shortcuts for strftime. + * + * A hash (object) of codes where each code must be an array where the first member is + * the name of a Date.prototype or jsDate.prototype function to call + * and optionally a second member indicating the number to pass to addZeros() + * + * <p>The following format codes are defined:</p> + * + * <pre class="code"> + * Code Result Description + * == Years == + * %Y 2008 Four-digit year + * %y 08 Two-digit year + * + * == Months == + * %m 09 Two-digit month + * %#m 9 One or two-digit month + * %B September Full month name + * %b Sep Abbreviated month name + * + * == Days == + * %d 05 Two-digit day of month + * %#d 5 One or two-digit day of month + * %e 5 One or two-digit day of month + * %A Sunday Full name of the day of the week + * %a Sun Abbreviated name of the day of the week + * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) + * + * == Hours == + * %H 23 Hours in 24-hour format (two digits) + * %#H 3 Hours in 24-hour integer format (one or two digits) + * %I 11 Hours in 12-hour format (two digits) + * %#I 3 Hours in 12-hour integer format (one or two digits) + * %p PM AM or PM + * + * == Minutes == + * %M 09 Minutes (two digits) + * %#M 9 Minutes (one or two digits) + * + * == Seconds == + * %S 02 Seconds (two digits) + * %#S 2 Seconds (one or two digits) + * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) + * + * == Milliseconds == + * %N 008 Milliseconds (three digits) + * %#N 8 Milliseconds (one to three digits) + * + * == Timezone == + * %O 360 difference in minutes between local time and GMT + * %Z Mountain Standard Time Name of timezone as reported by browser + * %G 06:00 Hours and minutes between GMT + * + * == Shortcuts == + * %F 2008-03-26 %Y-%m-%d + * %T 05:06:30 %H:%M:%S + * %X 05:06:30 %H:%M:%S + * %x 03/26/08 %m/%d/%y + * %D 03/26/08 %m/%d/%y + * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y + * %v 3-Sep-2008 %e-%b-%Y + * %R 15:31 %H:%M + * %r 03:31:00 PM %I:%M:%S %p + * + * == Characters == + * %n \n Newline + * %t \t Tab + * %% % Percent Symbol + * </pre> + * + * <p>Formatting shortcuts that will be translated into their longer version. + * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p> + * + * <p>Format codes and format shortcuts can be redefined after the jsDate + * module is imported.</p> + * + * <p>Note that if you redefine the whole hash (object), you must supply a "matcher" + * regex for the parser. The default matcher is:</p> + * + * <code>/()%(#?(%|[a-z]))/i</code> + * + * <p>which corresponds to the Perl syntax used by default.</p> + * + * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p> + */ + + jsDate.formats.perl = { + codes: { + // + // 2-part regex matcher for format codes + // + // first match must be the character before the code (to account for escaping) + // second match must be the format code character(s) + // + matcher: /()%(#?(%|[a-z]))/i, + // year + Y: 'FullYear', + y: 'ShortYear.2', + // month + m: 'MonthNumber.2', + '#m': 'MonthNumber', + B: 'MonthName', + b: 'AbbrMonthName', + // day + d: 'Date.2', + '#d': 'Date', + e: 'Date', + A: 'DayName', + a: 'AbbrDayName', + w: 'Day', + // hours + H: 'Hours.2', + '#H': 'Hours', + I: 'Hours12.2', + '#I': 'Hours12', + p: 'AMPM', + // minutes + M: 'Minutes.2', + '#M': 'Minutes', + // seconds + S: 'Seconds.2', + '#S': 'Seconds', + s: 'Unix', + // milliseconds + N: 'Milliseconds.3', + '#N': 'Milliseconds', + // timezone + O: 'TimezoneOffset', + Z: 'TimezoneName', + G: 'GmtOffset' + }, + + shortcuts: { + // date + F: '%Y-%m-%d', + // time + T: '%H:%M:%S', + X: '%H:%M:%S', + // local format date + x: '%m/%d/%y', + D: '%m/%d/%y', + // local format extended + '#c': '%a %b %e %H:%M:%S %Y', + // local format short + v: '%e-%b-%Y', + R: '%H:%M', + r: '%I:%M:%S %p', + // tab and newline + t: '\t', + n: '\n', + '%': '%' + } + }; + + /** + * PHP format codes and shortcuts for strftime. + * + * A hash (object) of codes where each code must be an array where the first member is + * the name of a Date.prototype or jsDate.prototype function to call + * and optionally a second member indicating the number to pass to addZeros() + * + * <p>The following format codes are defined:</p> + * + * <pre class="code"> + * Code Result Description + * === Days === + * %a Sun through Sat An abbreviated textual representation of the day + * %A Sunday - Saturday A full textual representation of the day + * %d 01 to 31 Two-digit day of the month (with leading zeros) + * %e 1 to 31 Day of the month, with a space preceding single digits. + * %j 001 to 366 Day of the year, 3 digits with leading zeros + * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week + * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week + * + * === Week === + * %U 13 Full Week number, starting with the first Sunday as the first week + * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year + * with at least 4 weekdays, with Monday being the start of the week + * %W 46 A numeric representation of the week of the year, + * starting with the first Monday as the first week + * === Month === + * %b Jan through Dec Abbreviated month name, based on the locale + * %B January - December Full month name, based on the locale + * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b) + * %m 01 - 12 (Jan - Dec) Two digit representation of the month + * + * === Year === + * %C 19 Two digit century (year/100, truncated to an integer) + * %y 09 for 2009 Two digit year + * %Y 2038 Four digit year + * + * === Time === + * %H 00 through 23 Two digit representation of the hour in 24-hour format + * %I 01 through 12 Two digit representation of the hour in 12-hour format + * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits + * %M 00 through 59 Two digit representation of the minute + * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time + * %P am/pm lower-case 'am' or 'pm' based on the given time + * %r 09:34:17 PM Same as %I:%M:%S %p + * %R 00:35 Same as %H:%M + * %S 00 through 59 Two digit representation of the second + * %T 21:34:17 Same as %H:%M:%S + * %X 03:59:16 Preferred time representation based on locale, without the date + * %z -0500 or EST Either the time zone offset from UTC or the abbreviation + * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z + * + * === Time and Date === + * %D 02/05/09 Same as %m/%d/%y + * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps) + * %s 305815200 Unix Epoch Time timestamp (same as the time() function) + * %x 02/05/09 Preferred date representation, without the time + * + * === Miscellaneous === + * %n --- A newline character (\n) + * %t --- A Tab character (\t) + * %% --- A literal percentage character (%) + * </pre> + */ + + jsDate.formats.php = { + codes: { + // + // 2-part regex matcher for format codes + // + // first match must be the character before the code (to account for escaping) + // second match must be the format code character(s) + // + matcher: /()%((%|[a-z]))/i, + // day + a: 'AbbrDayName', + A: 'DayName', + d: 'Date.2', + e: 'Date', + j: 'DayOfYear.3', + u: 'DayOfWeek', + w: 'Day', + // week + U: 'FullWeekOfYear.2', + V: 'IsoWeek.2', + W: 'WeekOfYear.2', + // month + b: 'AbbrMonthName', + B: 'MonthName', + m: 'MonthNumber.2', + h: 'AbbrMonthName', + // year + C: 'Century.2', + y: 'ShortYear.2', + Y: 'FullYear', + // time + H: 'Hours.2', + I: 'Hours12.2', + l: 'Hours12', + p: 'AMPM', + P: 'AmPm', + M: 'Minutes.2', + S: 'Seconds.2', + s: 'Unix', + O: 'TimezoneOffset', + z: 'GmtOffset', + Z: 'TimezoneAbbr' + }, + + shortcuts: { + D: '%m/%d/%y', + F: '%Y-%m-%d', + T: '%H:%M:%S', + X: '%H:%M:%S', + x: '%m/%d/%y', + R: '%H:%M', + r: '%I:%M:%S %p', + t: '\t', + n: '\n', + '%': '%' + } + }; + // + // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods. + // I use his idea of a set of parsers which can be regular expressions or functions, + // iterating through those, and then seeing if Date.parse() will create a date. + // The parser expressions and functions are a little different and some bugs have been + // worked out. Also, a lot of "pre-parsing" is done to fix implementation + // variations of Date.parse() between browsers. + // + jsDate.createDate = function(date) { + // if passing in multiple arguments, try Date constructor + if (date == null) { + return new Date(); + } + // If the passed value is already a date object, return it + if (date instanceof Date) { + return date; + } + // if (typeof date == 'number') return new Date(date * 1000); + // If the passed value is an integer, interpret it as a javascript timestamp + if (typeof date == 'number') { + return new Date(date); + } + + // Before passing strings into Date.parse(), have to normalize them for certain conditions. + // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent. + // + // For example: + // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century. + // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse. + // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime. + + // remove leading and trailing spaces + var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1'); + + // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn] + parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3"); + + ///////// + // Need to check for '15-Dec-09' also. + // FF will not parse, but Chrome will. + // Chrome will set date to 2009 as well. + ///////// + + // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010' + parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3"); + + // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century. + var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i); + if (match && match.length > 3) { + var m3 = parseFloat(match[3]); + var ny = jsDate.config.defaultCentury + m3; + ny = String(ny); + + // now replace 2 digit year with 4 digit year + parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny); + + } + + // Check for '1/19/70 8:14PM' + // where starts with mm/dd/yy or yy/mm/dd and have something after + // Check if 1st postiion is greater than 31, assume it is year. + // Assme all 2 digit years are 1900's. + // Finally, change them into US style mm/dd/yyyy representations. + match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/); + + function h1(parsable, match) { + var m1 = parseFloat(match[1]); + var m2 = parseFloat(match[2]); + var m3 = parseFloat(match[3]); + var cent = jsDate.config.defaultCentury; + var ny, nd, nm, str; + + if (m1 > 31) { // first number is a year + nd = m3; + nm = m2; + ny = cent + m1; + } + + else { // last number is the year + nd = m2; + nm = m1; + ny = cent + m3; + } + + str = nm+'/'+nd+'/'+ny; + + // now replace 2 digit year with 4 digit year + return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str); + + } + + if (match && match.length > 3) { + parsable = h1(parsable, match); + } + + // Now check for '1/19/70' with nothing after and do as above + var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/); + + if (match && match.length > 3) { + parsable = h1(parsable, match); + } + + + var i = 0; + var length = jsDate.matchers.length; + var pattern, + ms, + current = parsable, + obj; + while (i < length) { + ms = Date.parse(current); + if (!isNaN(ms)) { + return new Date(ms); + } + pattern = jsDate.matchers[i]; + if (typeof pattern == 'function') { + obj = pattern.call(jsDate, current); + if (obj instanceof Date) { + return obj; + } + } else { + current = parsable.replace(pattern[0], pattern[1]); + } + i++; + } + return NaN; + }; + + + /** + * @static + * Handy static utility function to return the number of days in a given month. + * @param {Integer} year Year + * @param {Integer} month Month (1-12) + * @returns {Integer} Number of days in the month. + */ + // + // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods. + // + jsDate.daysInMonth = function(year, month) { + if (month == 2) { + return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; + } + return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month]; + }; + + + // + // An Array of regular expressions or functions that will attempt to match the date string. + // Functions are called with scope of a jsDate instance. + // + jsDate.matchers = [ + // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date). + [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], + // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date). + [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], + // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part. + function(str) { + var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i); + // opt. date hour opt. minute opt. second opt. msec opt. am or pm + if (match) { + if (match[1]) { + var d = this.createDate(match[1]); + if (isNaN(d)) { + return; + } + } else { + var d = new Date(); + d.setMilliseconds(0); + } + var hour = parseFloat(match[2]); + if (match[6]) { + hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); + } + d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000); + return d; + } + else { + return str; + } + }, + // Handle ISO timestamp with time zone. + function(str) { + var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i); + if (match) { + if (match[1]) { + var d = this.createDate(match[1]); + if (isNaN(d)) { + return; + } + } else { + var d = new Date(); + d.setMilliseconds(0); + } + var hour = parseFloat(match[2]); + d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000); + return d; + } + else { + return str; + } + }, + // Try to match ambiguous strings like 12/8/22. + // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's). + // This may be redundant with pre processing of date already performed. + function(str) { + var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/); + if (match) { + var d = new Date(); + var cent = jsDate.config.defaultCentury; + var m1 = parseFloat(match[1]); + var m3 = parseFloat(match[3]); + var ny, nd, nm; + if (m1 > 31) { // first number is a year + nd = m3; + ny = cent + m1; + } + + else { // last number is the year + nd = m1; + ny = cent + m3; + } + + var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]); + + if (nm == -1) { + nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]); + } + + d.setFullYear(ny, nm, nd); + d.setHours(0,0,0,0); + return d; + } + + else { + return str; + } + } + ]; + + // + // I think John Reisig published this method on his blog, ejohn. + // + function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + } + + // + // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method. + // + function get_type(thing){ + if(thing===null) return "[object Null]"; // special case + return Object.prototype.toString.call(thing); + } + + $.jsDate = jsDate; + + + /** + * JavaScript printf/sprintf functions. + * + * This code has been adapted from the publicly available sprintf methods + * by Ash Searle. His original header follows: + * + * This code is unrestricted: you are free to use it however you like. + * + * The functions should work as expected, performing left or right alignment, + * truncating strings, outputting numbers with a required precision etc. + * + * For complex cases, these functions follow the Perl implementations of + * (s)printf, allowing arguments to be passed out-of-order, and to set the + * precision or length of the output based on arguments instead of fixed + * numbers. + * + * See http://perldoc.perl.org/functions/sprintf.html for more information. + * + * Implemented: + * - zero and space-padding + * - right and left-alignment, + * - base X prefix (binary, octal and hex) + * - positive number prefix + * - (minimum) width + * - precision / truncation / maximum width + * - out of order arguments + * + * Not implemented (yet): + * - vector flag + * - size (bytes, words, long-words etc.) + * + * Will not implement: + * - %n or %p (no pass-by-reference in JavaScript) + * + * @version 2007.04.27 + * @author Ash Searle + * + * You can see the original work and comments on his blog: + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + */ + + /** + * @Modifications 2009.05.26 + * @author Chris Leonello + * + * Added %p %P specifier + * Acts like %g or %G but will not add more significant digits to the output than present in the input. + * Example: + * Format: '%.3p', Input: 0.012, Output: 0.012 + * Format: '%.3g', Input: 0.012, Output: 0.0120 + * Format: '%.4p', Input: 12.0, Output: 12.0 + * Format: '%.4g', Input: 12.0, Output: 12.00 + * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5 + * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5 + * + * Example: + * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23) + * "23.35, 43" + * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524) + * "no value: , decimal with thousands separator: 433,524" + */ + $.jqplot.sprintf = function() { + function pad(str, len, chr, leftJustify) { + var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); + return leftJustify ? str + padding : padding + str; + + } + + function thousand_separate(value) { + var value_str = new String(value); + for (var i=10; i>0; i--) { + if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break; + } + return value_str; + } + + function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) { + var diff = minWidth - value.length; + if (diff > 0) { + var spchar = ' '; + if (htmlSpace) { spchar = ' '; } + if (leftJustify || !zeroPad) { + value = pad(value, minWidth, spchar, leftJustify); + } else { + value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); + } + } + return value; + } + + function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) { + // Note: casts negative numbers to positive ones + var number = value >>> 0; + prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || ''; + value = prefix + pad(number.toString(base), precision || 0, '0', false); + return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); + } + + function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) { + if (precision != null) { + value = value.slice(0, precision); + } + return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace); + } + + var a = arguments, i = 0, format = a[i++]; + + return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) { + if (substring == '%%') { return '%'; } + + // parse flags + var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false; + for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) { + case ' ': positivePrefix = ' '; break; + case '+': positivePrefix = '+'; break; + case '-': leftJustify = true; break; + case '0': zeroPad = true; break; + case '#': prefixBaseX = true; break; + case '&': htmlSpace = true; break; + case '\'': thousandSeparation = true; break; + } + + // parameters may be null, undefined, empty-string or real valued + // we want to ignore null, undefined and empty-string values + + if (!minWidth) { + minWidth = 0; + } + else if (minWidth == '*') { + minWidth = +a[i++]; + } + else if (minWidth.charAt(0) == '*') { + minWidth = +a[minWidth.slice(1, -1)]; + } + else { + minWidth = +minWidth; + } + + // Note: undocumented perl feature: + if (minWidth < 0) { + minWidth = -minWidth; + leftJustify = true; + } + + if (!isFinite(minWidth)) { + throw new Error('$.jqplot.sprintf: (minimum-)width must be finite'); + } + + if (!precision) { + precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0); + } + else if (precision == '*') { + precision = +a[i++]; + } + else if (precision.charAt(0) == '*') { + precision = +a[precision.slice(1, -1)]; + } + else { + precision = +precision; + } + + // grab value using valueIndex if required? + var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; + + switch (type) { + case 's': { + if (value == null) { + return ''; + } + return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace); + } + case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace); + case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace); + case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); + case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); + case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase(); + case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); + case 'i': { + var number = parseInt(+value, 10); + if (isNaN(number)) { + return ''; + } + var prefix = number < 0 ? '-' : positivePrefix; + var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); + value = prefix + pad(number_str, precision, '0', false); + //value = prefix + pad(String(Math.abs(number)), precision, '0', false); + return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); + } + case 'd': { + var number = Math.round(+value); + if (isNaN(number)) { + return ''; + } + var prefix = number < 0 ? '-' : positivePrefix; + var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); + value = prefix + pad(number_str, precision, '0', false); + return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); + } + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + { + var number = +value; + if (isNaN(number)) { + return ''; + } + var prefix = number < 0 ? '-' : positivePrefix; + var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; + var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; + var number_str = Math.abs(number)[method](precision); + + // Apply the decimal mark properly by splitting the number by the + // decimalMark, applying thousands separator, and then placing it + // back in. + var parts = number_str.toString().split('.'); + parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0]; + number_str = parts.join($.jqplot.sprintf.decimalMark); + + value = prefix + number_str; + var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); + + return justified; + } + case 'p': + case 'P': + { + // make sure number is a number + var number = +value; + if (isNaN(number)) { + return ''; + } + var prefix = number < 0 ? '-' : positivePrefix; + + var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); + var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length; + var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; + + if (Math.abs(number) < 1) { + if (sd + zeros <= precision) { + value = prefix + Math.abs(number).toPrecision(sd); + } + else { + if (sd <= precision - 1) { + value = prefix + Math.abs(number).toExponential(sd-1); + } + else { + value = prefix + Math.abs(number).toExponential(precision-1); + } + } + } + else { + var prec = (sd <= precision) ? sd : precision; + value = prefix + Math.abs(number).toPrecision(prec); + } + var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2]; + return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); + } + case 'n': return ''; + default: return substring; + } + }); + }; + + $.jqplot.sprintf.thousandsSeparator = ','; + // Specifies the decimal mark for floating point values. By default a period '.' + // is used. If you change this value to for example a comma be sure to also + // change the thousands separator or else this won't work since a simple String + // replace is used (replacing all periods with the mark specified here). + $.jqplot.sprintf.decimalMark = '.'; + + $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g; + + $.jqplot.getSignificantFigures = function(number) { + var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); + // total significant digits + var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length; + var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; + // exponent + var expn = parseInt(parts[1], 10); + // digits to the left of the decimal place + var dleft = (expn + 1 > 0) ? expn + 1 : 0; + // digits to the right of the decimal place + var dright = (sd <= dleft) ? 0 : sd - expn - 1; + return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ; + }; + + $.jqplot.getPrecision = function(number) { + return $.jqplot.getSignificantFigures(number).digitsRight; + }; + + + + + var backCompat = $.uiBackCompat !== false; + + $.jqplot.effects = { + effect: {} + }; + + // prefix used for storing data on .data() + var dataSpace = "jqplot.storage."; + + /******************************************************************************/ + /*********************************** EFFECTS **********************************/ + /******************************************************************************/ + + $.extend( $.jqplot.effects, { + version: "1.9pre", + + // Saves a set of properties in a data storage + save: function( element, set ) { + for( var i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); + } + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function( element, set ) { + for( var i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + element.css( set[ i ], element.data( dataSpace + set[ i ] ) ); + } + } + }, + + setMode: function( el, mode ) { + if (mode === "toggle") { + mode = el.is( ":hidden" ) ? "show" : "hide"; + } + return mode; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function( element ) { + + // if the element is already wrapped, return it + if ( element.parent().is( ".ui-effects-wrapper" )) { + return element.parent(); + } + + // wrap the element + var props = { + width: element.outerWidth(true), + height: element.outerHeight(true), + "float": element.css( "float" ) + }, + wrapper = $( "<div></div>" ) + .addClass( "ui-effects-wrapper" ) + .css({ + fontSize: "100%", + background: "transparent", + border: "none", + margin: 0, + padding: 0 + }), + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }, + active = document.activeElement; + + element.wrap( wrapper ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + + wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element + + // transfer positioning properties to the wrapper + if ( element.css( "position" ) === "static" ) { + wrapper.css({ position: "relative" }); + element.css({ position: "relative" }); + } else { + $.extend( props, { + position: element.css( "position" ), + zIndex: element.css( "z-index" ) + }); + $.each([ "top", "left", "bottom", "right" ], function(i, pos) { + props[ pos ] = element.css( pos ); + if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { + props[ pos ] = "auto"; + } + }); + element.css({ + position: "relative", + top: 0, + left: 0, + right: "auto", + bottom: "auto" + }); + } + element.css(size); + + return wrapper.css( props ).show(); + }, + + removeWrapper: function( element ) { + var active = document.activeElement; + + if ( element.parent().is( ".ui-effects-wrapper" ) ) { + element.parent().replaceWith( element ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + } + + + return element; + } + }); + + // return an effect options object for the given parameters: + function _normalizeArguments( effect, options, speed, callback ) { + + // short path for passing an effect options object: + if ( $.isPlainObject( effect ) ) { + return effect; + } + + // convert to an object + effect = { effect: effect }; + + // catch (effect) + if ( options === undefined ) { + options = {}; + } + + // catch (effect, callback) + if ( $.isFunction( options ) ) { + callback = options; + speed = null; + options = {}; + } + + // catch (effect, speed, ?) + if ( $.type( options ) === "number" || $.fx.speeds[ options ]) { + callback = speed; + speed = options; + options = {}; + } + + // catch (effect, options, callback) + if ( $.isFunction( speed ) ) { + callback = speed; + speed = null; + } + + // add options to effect + if ( options ) { + $.extend( effect, options ); + } + + speed = speed || options.duration; + effect.duration = $.fx.off ? 0 : typeof speed === "number" + ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default; + + effect.complete = callback || options.complete; + + return effect; + } + + function standardSpeed( speed ) { + // valid standard speeds + if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { + return true; + } + + // invalid strings - treat as "normal" speed + if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) { + // TODO: remove in 2.0 (#7115) + if ( backCompat && $.jqplot.effects[ speed ] ) { + return false; + } + return true; + } + + return false; + } + + $.fn.extend({ + jqplotEffect: function( effect, options, speed, callback ) { + var args = _normalizeArguments.apply( this, arguments ), + mode = args.mode, + queue = args.queue, + effectMethod = $.jqplot.effects.effect[ args.effect ], + + // DEPRECATED: remove in 2.0 (#7115) + oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ]; + + if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) { + // delegate to the original method (e.g., .show()) if possible + if ( mode ) { + return this[ mode ]( args.duration, args.complete ); + } else { + return this.each( function() { + if ( args.complete ) { + args.complete.call( this ); + } + }); + } + } + + function run( next ) { + var elem = $( this ), + complete = args.complete, + mode = args.mode; + + function done() { + if ( $.isFunction( complete ) ) { + complete.call( elem[0] ); + } + if ( $.isFunction( next ) ) { + next(); + } + } + + // if the element is hiddden and mode is hide, + // or element is visible and mode is show + if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { + done(); + } else { + effectMethod.call( elem[0], args, done ); + } + } + + // TODO: remove this check in 2.0, effectMethod will always be true + if ( effectMethod ) { + return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); + } else { + // DEPRECATED: remove in 2.0 (#7115) + return oldEffectMethod.call(this, { + options: args, + duration: args.duration, + callback: args.complete, + mode: args.mode + }); + } + } + }); + + + + + var rvertical = /up|down|vertical/, + rpositivemotion = /up|left|vertical|horizontal/; + + $.jqplot.effects.effect.blind = function( o, done ) { + // Create element + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.jqplot.effects.setMode( el, o.mode || "hide" ), + direction = o.direction || "up", + vertical = rvertical.test( direction ), + ref = vertical ? "height" : "width", + ref2 = vertical ? "top" : "left", + motion = rpositivemotion.test( direction ), + animation = {}, + show = mode === "show", + wrapper, distance, top; + + // // if already wrapped, the wrapper's properties are my property. #6245 + if ( el.parent().is( ".ui-effects-wrapper" ) ) { + $.jqplot.effects.save( el.parent(), props ); + } else { + $.jqplot.effects.save( el, props ); + } + el.show(); + top = parseInt(el.css('top'), 10); + wrapper = $.jqplot.effects.createWrapper( el ).css({ + overflow: "hidden" + }); + + distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ](); + + animation[ ref ] = show ? String(distance) : '0'; + if ( !motion ) { + el + .css( vertical ? "bottom" : "right", 0 ) + .css( vertical ? "top" : "left", "" ) + .css({ position: "absolute" }); + animation[ ref2 ] = show ? '0' : String(distance); + } + + // // start at 0 if we are showing + if ( show ) { + wrapper.css( ref, 0 ); + if ( ! motion ) { + wrapper.css( ref2, distance ); + } + } + + // // Animate + wrapper.animate( animation, { + duration: o.duration, + easing: o.easing, + queue: false, + complete: function() { + if ( mode === "hide" ) { + el.hide(); + } + $.jqplot.effects.restore( el, props ); + $.jqplot.effects.removeWrapper( el ); + done(); + } + }); + + }; + +})(jQuery); diff --git a/web/extensions/jqplot/jquery.jqplot.min.css b/web/extensions/jqplot/jquery.jqplot.min.css new file mode 100644 index 0000000..0f84835 --- /dev/null +++ b/web/extensions/jqplot/jquery.jqplot.min.css @@ -0,0 +1 @@ +.jqplot-target{position:relative;color:#666;font-family:"Trebuchet MS",Arial,Helvetica,sans-serif;font-size:1em}.jqplot-axis{font-size:.75em}.jqplot-xaxis{margin-top:10px}.jqplot-x2axis{margin-bottom:10px}.jqplot-yaxis{margin-right:10px}.jqplot-y2axis,.jqplot-y3axis,.jqplot-y4axis,.jqplot-y5axis,.jqplot-y6axis,.jqplot-y7axis,.jqplot-y8axis,.jqplot-y9axis,.jqplot-yMidAxis{margin-left:10px;margin-right:10px}.jqplot-axis-tick,.jqplot-xaxis-tick,.jqplot-yaxis-tick,.jqplot-x2axis-tick,.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick,.jqplot-yMidAxis-tick{position:absolute;white-space:pre}.jqplot-xaxis-tick{top:0;left:15px;vertical-align:top}.jqplot-x2axis-tick{bottom:0;left:15px;vertical-align:bottom}.jqplot-yaxis-tick{right:0;top:15px;text-align:right}.jqplot-yaxis-tick.jqplot-breakTick{right:-20px;margin-right:0;padding:1px 5px 1px 5px;z-index:2;font-size:1.5em}.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick{left:0;top:15px;text-align:left}.jqplot-yMidAxis-tick{text-align:center;white-space:nowrap}.jqplot-xaxis-label{margin-top:10px;font-size:11pt;position:absolute}.jqplot-x2axis-label{margin-bottom:10px;font-size:11pt;position:absolute}.jqplot-yaxis-label{margin-right:10px;font-size:11pt;position:absolute}.jqplot-yMidAxis-label{font-size:11pt;position:absolute}.jqplot-y2axis-label,.jqplot-y3axis-label,.jqplot-y4axis-label,.jqplot-y5axis-label,.jqplot-y6axis-label,.jqplot-y7axis-label,.jqplot-y8axis-label,.jqplot-y9axis-label{font-size:11pt;margin-left:10px;position:absolute}.jqplot-meterGauge-tick{font-size:.75em;color:#999}.jqplot-meterGauge-label{font-size:1em;color:#999}table.jqplot-table-legend{margin-top:12px;margin-bottom:12px;margin-left:12px;margin-right:12px}table.jqplot-table-legend,table.jqplot-cursor-legend{background-color:rgba(255,255,255,0.6);border:1px solid #ccc;position:absolute;font-size:.75em}td.jqplot-table-legend{vertical-align:middle}td.jqplot-seriesToggle:hover,td.jqplot-seriesToggle:active{cursor:pointer}.jqplot-table-legend .jqplot-series-hidden{text-decoration:line-through}div.jqplot-table-legend-swatch-outline{border:1px solid #ccc;padding:1px}div.jqplot-table-legend-swatch{width:0;height:0;border-top-width:5px;border-bottom-width:5px;border-left-width:6px;border-right-width:6px;border-top-style:solid;border-bottom-style:solid;border-left-style:solid;border-right-style:solid}.jqplot-title{top:0;left:0;padding-bottom:.5em;font-size:1.2em}table.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em}.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px}.jqplot-highlighter-tooltip,.jqplot-canvasOverlay-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px}.jqplot-point-label{font-size:.75em;z-index:2}td.jqplot-cursor-legend-swatch{vertical-align:middle;text-align:center}div.jqplot-cursor-legend-swatch{width:1.2em;height:.7em}.jqplot-error{text-align:center}.jqplot-error-message{position:relative;top:46%;display:inline-block}div.jqplot-bubble-label{font-size:.8em;padding-left:2px;padding-right:2px;color:rgb(20%,20%,20%)}div.jqplot-bubble-label.jqplot-bubble-label-highlight{background:rgba(90%,90%,90%,0.7)}div.jqplot-noData-container{text-align:center;background-color:rgba(96%,96%,96%,0.3)} \ No newline at end of file diff --git a/web/extensions/jqplot/jquery.jqplot.min.js b/web/extensions/jqplot/jquery.jqplot.min.js new file mode 100644 index 0000000..8facc6c --- /dev/null +++ b/web/extensions/jqplot/jquery.jqplot.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(L){var u;L.fn.emptyForce=function(){for(var ah=0,ai;(ai=L(this)[ah])!=null;ah++){if(ai.nodeType===1){L.cleanData(ai.getElementsByTagName("*"))}if(L.jqplot.use_excanvas){ai.outerHTML=""}else{while(ai.firstChild){ai.removeChild(ai.firstChild)}}ai=null}return L(this)};L.fn.removeChildForce=function(ah){while(ah.firstChild){this.removeChildForce(ah.firstChild);ah.removeChild(ah.firstChild)}};L.fn.jqplot=function(){var ah=[];var aj=[];for(var ak=0,ai=arguments.length;ak<ai;ak++){if(L.isArray(arguments[ak])){ah.push(arguments[ak])}else{if(L.isPlainObject(arguments[ak])){aj.push(arguments[ak])}}}return this.each(function(an){var at,ar,aq=L(this),am=ah.length,al=aj.length,ap,ao;if(an<am){ap=ah[an]}else{ap=am?ah[am-1]:null}if(an<al){ao=aj[an]}else{ao=al?aj[al-1]:null}at=aq.attr("id");if(at===u){at="jqplot_target_"+L.jqplot.targetCounter++;aq.attr("id",at)}ar=L.jqplot(at,ap,ao);aq.data("jqplot",ar)})};L.jqplot=function(an,ak,ai){var aj=null,ah=null;if(arguments.length===3){aj=ak;ah=ai}else{if(arguments.length===2){if(L.isArray(ak)){aj=ak}else{if(L.isPlainObject(ak)){ah=ak}}}}if(aj===null&&ah!==null&&ah.data){aj=ah.data}var am=new R();L("#"+an).removeClass("jqplot-error");if(L.jqplot.config.catchErrors){try{am.init(an,aj,ah);am.draw();am.themeEngine.init.call(am);return am}catch(al){var ao=L.jqplot.config.errorMessage||al.message;L("#"+an).append('<div class="jqplot-error-message">'+ao+"</div>");L("#"+an).addClass("jqplot-error");document.getElementById(an).style.background=L.jqplot.config.errorBackground;document.getElementById(an).style.border=L.jqplot.config.errorBorder;document.getElementById(an).style.fontFamily=L.jqplot.config.errorFontFamily;document.getElementById(an).style.fontSize=L.jqplot.config.errorFontSize;document.getElementById(an).style.fontStyle=L.jqplot.config.errorFontStyle;document.getElementById(an).style.fontWeight=L.jqplot.config.errorFontWeight}}else{am.init(an,aj,ah);am.draw();am.themeEngine.init.call(am);return am}};L.jqplot.version="1.0.8";L.jqplot.revision="1250";L.jqplot.targetCounter=1;L.jqplot.CanvasManager=function(){if(typeof L.jqplot.CanvasManager.canvases=="undefined"){L.jqplot.CanvasManager.canvases=[];L.jqplot.CanvasManager.free=[]}var ah=[];this.getCanvas=function(){var ak;var aj=true;if(!L.jqplot.use_excanvas){for(var al=0,ai=L.jqplot.CanvasManager.canvases.length;al<ai;al++){if(L.jqplot.CanvasManager.free[al]===true){aj=false;ak=L.jqplot.CanvasManager.canvases[al];L.jqplot.CanvasManager.free[al]=false;ah.push(al);break}}}if(aj){ak=document.createElement("canvas");ah.push(L.jqplot.CanvasManager.canvases.length);L.jqplot.CanvasManager.canvases.push(ak);L.jqplot.CanvasManager.free.push(false)}return ak};this.initCanvas=function(ai){if(L.jqplot.use_excanvas){return window.G_vmlCanvasManager.initElement(ai)}return ai};this.freeAllCanvases=function(){for(var aj=0,ai=ah.length;aj<ai;aj++){this.freeCanvas(ah[aj])}ah=[]};this.freeCanvas=function(ai){if(L.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==u){window.G_vmlCanvasManager.uninitElement(L.jqplot.CanvasManager.canvases[ai]);L.jqplot.CanvasManager.canvases[ai]=null}else{var aj=L.jqplot.CanvasManager.canvases[ai];aj.getContext("2d").clearRect(0,0,aj.width,aj.height);L(aj).unbind().removeAttr("class").removeAttr("style");L(aj).css({left:"",top:"",position:""});aj.width=0;aj.height=0;L.jqplot.CanvasManager.free[ai]=true}}};L.jqplot.log=function(){if(window.console){window.console.log.apply(window.console,arguments)}};L.jqplot.config={addDomReference:false,enablePlugins:false,defaultHeight:300,defaultWidth:400,UTCAdjust:false,timezoneOffset:new Date(new Date().getTimezoneOffset()*60000),errorMessage:"",errorBackground:"",errorBorder:"",errorFontFamily:"",errorFontSize:"",errorFontStyle:"",errorFontWeight:"",catchErrors:false,defaultTickFormatString:"%.1f",defaultColors:["#4bb2c5","#EAA228","#c5b47f","#579575","#839557","#958c12","#953579","#4b5de4","#d8b83f","#ff5800","#0085cc","#c747a3","#cddf54","#FBD178","#26B4E3","#bd70c7"],defaultNegativeColors:["#498991","#C08840","#9F9274","#546D61","#646C4A","#6F6621","#6E3F5F","#4F64B0","#A89050","#C45923","#187399","#945381","#959E5C","#C7AF7B","#478396","#907294"],dashLength:4,gapLength:4,dotGapLength:2.5,srcLocation:"jqplot/src/",pluginLocation:"jqplot/src/plugins/"};L.jqplot.arrayMax=function(ah){return Math.max.apply(Math,ah)};L.jqplot.arrayMin=function(ah){return Math.min.apply(Math,ah)};L.jqplot.enablePlugins=L.jqplot.config.enablePlugins;L.jqplot.support_canvas=function(){if(typeof L.jqplot.support_canvas.result=="undefined"){L.jqplot.support_canvas.result=!!document.createElement("canvas").getContext}return L.jqplot.support_canvas.result};L.jqplot.support_canvas_text=function(){if(typeof L.jqplot.support_canvas_text.result=="undefined"){if(window.G_vmlCanvasManager!==u&&window.G_vmlCanvasManager._version>887){L.jqplot.support_canvas_text.result=true}else{L.jqplot.support_canvas_text.result=!!(document.createElement("canvas").getContext&&typeof document.createElement("canvas").getContext("2d").fillText=="function")}}return L.jqplot.support_canvas_text.result};L.jqplot.use_excanvas=((!L.support.boxModel||!L.support.objectAll||!$support.leadingWhitespace)&&!L.jqplot.support_canvas())?true:false;L.jqplot.preInitHooks=[];L.jqplot.postInitHooks=[];L.jqplot.preParseOptionsHooks=[];L.jqplot.postParseOptionsHooks=[];L.jqplot.preDrawHooks=[];L.jqplot.postDrawHooks=[];L.jqplot.preDrawSeriesHooks=[];L.jqplot.postDrawSeriesHooks=[];L.jqplot.preDrawLegendHooks=[];L.jqplot.addLegendRowHooks=[];L.jqplot.preSeriesInitHooks=[];L.jqplot.postSeriesInitHooks=[];L.jqplot.preParseSeriesOptionsHooks=[];L.jqplot.postParseSeriesOptionsHooks=[];L.jqplot.eventListenerHooks=[];L.jqplot.preDrawSeriesShadowHooks=[];L.jqplot.postDrawSeriesShadowHooks=[];L.jqplot.ElemContainer=function(){this._elem;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null}};L.jqplot.ElemContainer.prototype.createElement=function(ak,am,ai,aj,an){this._offsets=am;var ah=ai||"jqplot";var al=document.createElement(ak);this._elem=L(al);this._elem.addClass(ah);this._elem.css(aj);this._elem.attr(an);al=null;return this._elem};L.jqplot.ElemContainer.prototype.getWidth=function(){if(this._elem){return this._elem.outerWidth(true)}else{return null}};L.jqplot.ElemContainer.prototype.getHeight=function(){if(this._elem){return this._elem.outerHeight(true)}else{return null}};L.jqplot.ElemContainer.prototype.getPosition=function(){if(this._elem){return this._elem.position()}else{return{top:null,left:null,bottom:null,right:null}}};L.jqplot.ElemContainer.prototype.getTop=function(){return this.getPosition().top};L.jqplot.ElemContainer.prototype.getLeft=function(){return this.getPosition().left};L.jqplot.ElemContainer.prototype.getBottom=function(){return this._elem.css("bottom")};L.jqplot.ElemContainer.prototype.getRight=function(){return this._elem.css("right")};function w(ah){L.jqplot.ElemContainer.call(this);this.name=ah;this._series=[];this.show=false;this.tickRenderer=L.jqplot.AxisTickRenderer;this.tickOptions={};this.labelRenderer=L.jqplot.AxisLabelRenderer;this.labelOptions={};this.label=null;this.showLabel=true;this.min=null;this.max=null;this.autoscale=false;this.pad=1.2;this.padMax=null;this.padMin=null;this.ticks=[];this.numberTicks;this.tickInterval;this.renderer=L.jqplot.LinearAxisRenderer;this.rendererOptions={};this.showTicks=true;this.showTickMarks=true;this.showMinorTicks=true;this.drawMajorGridlines=true;this.drawMinorGridlines=false;this.drawMajorTickMarks=true;this.drawMinorTickMarks=true;this.useSeriesColor=false;this.borderWidth=null;this.borderColor=null;this.scaleToHiddenSeries=false;this._dataBounds={min:null,max:null};this._intervalStats=[];this._offsets={min:null,max:null};this._ticks=[];this._label=null;this.syncTicks=null;this.tickSpacing=75;this._min=null;this._max=null;this._tickInterval=null;this._numberTicks=null;this.__ticks=null;this._options={}}w.prototype=new L.jqplot.ElemContainer();w.prototype.constructor=w;w.prototype.init=function(){if(L.isFunction(this.renderer)){this.renderer=new this.renderer()}this.tickOptions.axis=this.name;if(this.tickOptions.showMark==null){this.tickOptions.showMark=this.showTicks}if(this.tickOptions.showMark==null){this.tickOptions.showMark=this.showTickMarks}if(this.tickOptions.showLabel==null){this.tickOptions.showLabel=this.showTicks}if(this.label==null||this.label==""){this.showLabel=false}else{this.labelOptions.label=this.label}if(this.showLabel==false){this.labelOptions.show=false}if(this.pad==0){this.pad=1}if(this.padMax==0){this.padMax=1}if(this.padMin==0){this.padMin=1}if(this.padMax==null){this.padMax=(this.pad-1)/2+1}if(this.padMin==null){this.padMin=(this.pad-1)/2+1}this.pad=this.padMax+this.padMin-1;if(this.min!=null||this.max!=null){this.autoscale=false}if(this.syncTicks==null&&this.name.indexOf("y")>-1){this.syncTicks=true}else{if(this.syncTicks==null){this.syncTicks=false}}this.renderer.init.call(this,this.rendererOptions)};w.prototype.draw=function(ah,ai){if(this.__ticks){this.__ticks=null}return this.renderer.draw.call(this,ah,ai)};w.prototype.set=function(){this.renderer.set.call(this)};w.prototype.pack=function(ai,ah){if(this.show){this.renderer.pack.call(this,ai,ah)}if(this._min==null){this._min=this.min;this._max=this.max;this._tickInterval=this.tickInterval;this._numberTicks=this.numberTicks;this.__ticks=this._ticks}};w.prototype.reset=function(){this.renderer.reset.call(this)};w.prototype.resetScale=function(ah){L.extend(true,this,{min:null,max:null,numberTicks:null,tickInterval:null,_ticks:[],ticks:[]},ah);this.resetDataBounds()};w.prototype.resetDataBounds=function(){var ao=this._dataBounds;ao.min=null;ao.max=null;var ai,ap,am;var aj=(this.show)?true:false;for(var al=0;al<this._series.length;al++){ap=this._series[al];if(ap.show||this.scaleToHiddenSeries){am=ap._plotData;if(ap._type==="line"&&ap.renderer.bands.show&&this.name.charAt(0)!=="x"){am=[[0,ap.renderer.bands._min],[1,ap.renderer.bands._max]]}var ah=1,an=1;if(ap._type!=null&&ap._type=="ohlc"){ah=3;an=2}for(var ak=0,ai=am.length;ak<ai;ak++){if(this.name=="xaxis"||this.name=="x2axis"){if((am[ak][0]!=null&&am[ak][0]<ao.min)||ao.min==null){ao.min=am[ak][0]}if((am[ak][0]!=null&&am[ak][0]>ao.max)||ao.max==null){ao.max=am[ak][0]}}else{if((am[ak][ah]!=null&&am[ak][ah]<ao.min)||ao.min==null){ao.min=am[ak][ah]}if((am[ak][an]!=null&&am[ak][an]>ao.max)||ao.max==null){ao.max=am[ak][an]}}}if(aj&&ap.renderer.constructor!==L.jqplot.BarRenderer){aj=false}else{if(aj&&this._options.hasOwnProperty("forceTickAt0")&&this._options.forceTickAt0==false){aj=false}else{if(aj&&ap.renderer.constructor===L.jqplot.BarRenderer){if(ap.barDirection=="vertical"&&this.name!="xaxis"&&this.name!="x2axis"){if(this._options.pad!=null||this._options.padMin!=null){aj=false}}else{if(ap.barDirection=="horizontal"&&(this.name=="xaxis"||this.name=="x2axis")){if(this._options.pad!=null||this._options.padMin!=null){aj=false}}}}}}}}if(aj&&this.renderer.constructor===L.jqplot.LinearAxisRenderer&&ao.min>=0){this.padMin=1;this.forceTickAt0=true}};function q(ah){L.jqplot.ElemContainer.call(this);this.show=false;this.location="ne";this.labels=[];this.showLabels=true;this.showSwatches=true;this.placement="insideGrid";this.xoffset=0;this.yoffset=0;this.border;this.background;this.textColor;this.fontFamily;this.fontSize;this.rowSpacing="0.5em";this.renderer=L.jqplot.TableLegendRenderer;this.rendererOptions={};this.preDraw=false;this.marginTop=null;this.marginRight=null;this.marginBottom=null;this.marginLeft=null;this.escapeHtml=false;this._series=[];L.extend(true,this,ah)}q.prototype=new L.jqplot.ElemContainer();q.prototype.constructor=q;q.prototype.setOptions=function(ah){L.extend(true,this,ah);if(this.placement=="inside"){this.placement="insideGrid"}if(this.xoffset>0){if(this.placement=="insideGrid"){switch(this.location){case"nw":case"w":case"sw":if(this.marginLeft==null){this.marginLeft=this.xoffset+"px"}this.marginRight="0px";break;case"ne":case"e":case"se":default:if(this.marginRight==null){this.marginRight=this.xoffset+"px"}this.marginLeft="0px";break}}else{if(this.placement=="outside"){switch(this.location){case"nw":case"w":case"sw":if(this.marginRight==null){this.marginRight=this.xoffset+"px"}this.marginLeft="0px";break;case"ne":case"e":case"se":default:if(this.marginLeft==null){this.marginLeft=this.xoffset+"px"}this.marginRight="0px";break}}}this.xoffset=0}if(this.yoffset>0){if(this.placement=="outside"){switch(this.location){case"sw":case"s":case"se":if(this.marginTop==null){this.marginTop=this.yoffset+"px"}this.marginBottom="0px";break;case"ne":case"n":case"nw":default:if(this.marginBottom==null){this.marginBottom=this.yoffset+"px"}this.marginTop="0px";break}}else{if(this.placement=="insideGrid"){switch(this.location){case"sw":case"s":case"se":if(this.marginBottom==null){this.marginBottom=this.yoffset+"px"}this.marginTop="0px";break;case"ne":case"n":case"nw":default:if(this.marginTop==null){this.marginTop=this.yoffset+"px"}this.marginBottom="0px";break}}}this.yoffset=0}};q.prototype.init=function(){if(L.isFunction(this.renderer)){this.renderer=new this.renderer()}this.renderer.init.call(this,this.rendererOptions)};q.prototype.draw=function(ai,aj){for(var ah=0;ah<L.jqplot.preDrawLegendHooks.length;ah++){L.jqplot.preDrawLegendHooks[ah].call(this,ai)}return this.renderer.draw.call(this,ai,aj)};q.prototype.pack=function(ah){this.renderer.pack.call(this,ah)};function y(ah){L.jqplot.ElemContainer.call(this);this.text=ah;this.show=true;this.fontFamily;this.fontSize;this.textAlign;this.textColor;this.renderer=L.jqplot.DivTitleRenderer;this.rendererOptions={};this.escapeHtml=false}y.prototype=new L.jqplot.ElemContainer();y.prototype.constructor=y;y.prototype.init=function(){if(L.isFunction(this.renderer)){this.renderer=new this.renderer()}this.renderer.init.call(this,this.rendererOptions)};y.prototype.draw=function(ah){return this.renderer.draw.call(this,ah)};y.prototype.pack=function(){this.renderer.pack.call(this)};function S(ah){ah=ah||{};L.jqplot.ElemContainer.call(this);this.show=true;this.xaxis="xaxis";this._xaxis;this.yaxis="yaxis";this._yaxis;this.gridBorderWidth=2;this.renderer=L.jqplot.LineRenderer;this.rendererOptions={};this.data=[];this.gridData=[];this.label="";this.showLabel=true;this.color;this.negativeColor;this.lineWidth=2.5;this.lineJoin="round";this.lineCap="round";this.linePattern="solid";this.shadow=true;this.shadowAngle=45;this.shadowOffset=1.25;this.shadowDepth=3;this.shadowAlpha="0.1";this.breakOnNull=false;this.markerRenderer=L.jqplot.MarkerRenderer;this.markerOptions={};this.showLine=true;this.showMarker=true;this.index;this.fill=false;this.fillColor;this.fillAlpha;this.fillAndStroke=false;this.disableStack=false;this._stack=false;this.neighborThreshold=4;this.fillToZero=false;this.fillToValue=0;this.fillAxis="y";this.useNegativeColors=true;this._stackData=[];this._plotData=[];this._plotValues={x:[],y:[]};this._intervals={x:{},y:{}};this._prevPlotData=[];this._prevGridData=[];this._stackAxis="y";this._primaryAxis="_xaxis";this.canvas=new L.jqplot.GenericCanvas();this.shadowCanvas=new L.jqplot.GenericCanvas();this.plugins={};this._sumy=0;this._sumx=0;this._type=""}S.prototype=new L.jqplot.ElemContainer();S.prototype.constructor=S;S.prototype.init=function(ak,ao,am){this.index=ak;this.gridBorderWidth=ao;var an=this.data;var aj=[],al,ah;for(al=0,ah=an.length;al<ah;al++){if(!this.breakOnNull){if(an[al]==null||an[al][0]==null||an[al][1]==null){continue}else{aj.push(an[al])}}else{aj.push(an[al])}}this.data=aj;if(!this.color){this.color=am.colorGenerator.get(this.index)}if(!this.negativeColor){this.negativeColor=am.negativeColorGenerator.get(this.index)}if(!this.fillColor){this.fillColor=this.color}if(this.fillAlpha){var ai=L.jqplot.normalize2rgb(this.fillColor);var ai=L.jqplot.getColorComponents(ai);this.fillColor="rgba("+ai[0]+","+ai[1]+","+ai[2]+","+this.fillAlpha+")"}if(L.isFunction(this.renderer)){this.renderer=new this.renderer()}this.renderer.init.call(this,this.rendererOptions,am);this.markerRenderer=new this.markerRenderer();if(!this.markerOptions.color){this.markerOptions.color=this.color}if(this.markerOptions.show==null){this.markerOptions.show=this.showMarker}this.showMarker=this.markerOptions.show;this.markerRenderer.init(this.markerOptions)};S.prototype.draw=function(an,ak,am){var ai=(ak==u)?{}:ak;an=(an==u)?this.canvas._ctx:an;var ah,al,aj;for(ah=0;ah<L.jqplot.preDrawSeriesHooks.length;ah++){L.jqplot.preDrawSeriesHooks[ah].call(this,an,ai)}if(this.show){this.renderer.setGridData.call(this,am);if(!ai.preventJqPlotSeriesDrawTrigger){L(an.canvas).trigger("jqplotSeriesDraw",[this.data,this.gridData])}al=[];if(ai.data){al=ai.data}else{if(!this._stack){al=this.data}else{al=this._plotData}}aj=ai.gridData||this.renderer.makeGridData.call(this,al,am);if(this._type==="line"&&this.renderer.smooth&&this.renderer._smoothedData.length){aj=this.renderer._smoothedData}this.renderer.draw.call(this,an,aj,ai,am)}for(ah=0;ah<L.jqplot.postDrawSeriesHooks.length;ah++){L.jqplot.postDrawSeriesHooks[ah].call(this,an,ai,am)}an=ak=am=ah=al=aj=null};S.prototype.drawShadow=function(an,ak,am){var ai=(ak==u)?{}:ak;an=(an==u)?this.shadowCanvas._ctx:an;var ah,al,aj;for(ah=0;ah<L.jqplot.preDrawSeriesShadowHooks.length;ah++){L.jqplot.preDrawSeriesShadowHooks[ah].call(this,an,ai)}if(this.shadow){this.renderer.setGridData.call(this,am);al=[];if(ai.data){al=ai.data}else{if(!this._stack){al=this.data}else{al=this._plotData}}aj=ai.gridData||this.renderer.makeGridData.call(this,al,am);this.renderer.drawShadow.call(this,an,aj,ai,am)}for(ah=0;ah<L.jqplot.postDrawSeriesShadowHooks.length;ah++){L.jqplot.postDrawSeriesShadowHooks[ah].call(this,an,ai)}an=ak=am=ah=al=aj=null};S.prototype.toggleDisplay=function(ai,ak){var ah,aj;if(ai.data.series){ah=ai.data.series}else{ah=this}if(ai.data.speed){aj=ai.data.speed}if(aj){if(ah.canvas._elem.is(":hidden")||!ah.show){ah.show=true;ah.canvas._elem.removeClass("jqplot-series-hidden");if(ah.shadowCanvas._elem){ah.shadowCanvas._elem.fadeIn(aj)}ah.canvas._elem.fadeIn(aj,ak);ah.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+ah.index).fadeIn(aj)}else{ah.show=false;ah.canvas._elem.addClass("jqplot-series-hidden");if(ah.shadowCanvas._elem){ah.shadowCanvas._elem.fadeOut(aj)}ah.canvas._elem.fadeOut(aj,ak);ah.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+ah.index).fadeOut(aj)}}else{if(ah.canvas._elem.is(":hidden")||!ah.show){ah.show=true;ah.canvas._elem.removeClass("jqplot-series-hidden");if(ah.shadowCanvas._elem){ah.shadowCanvas._elem.show()}ah.canvas._elem.show(0,ak);ah.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+ah.index).show()}else{ah.show=false;ah.canvas._elem.addClass("jqplot-series-hidden");if(ah.shadowCanvas._elem){ah.shadowCanvas._elem.hide()}ah.canvas._elem.hide(0,ak);ah.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+ah.index).hide()}}};function M(){L.jqplot.ElemContainer.call(this);this.drawGridlines=true;this.gridLineColor="#cccccc";this.gridLineWidth=1;this.background="#fffdf6";this.borderColor="#999999";this.borderWidth=2;this.drawBorder=true;this.shadow=true;this.shadowAngle=45;this.shadowOffset=1.5;this.shadowWidth=3;this.shadowDepth=3;this.shadowColor=null;this.shadowAlpha="0.07";this._left;this._top;this._right;this._bottom;this._width;this._height;this._axes=[];this.renderer=L.jqplot.CanvasGridRenderer;this.rendererOptions={};this._offsets={top:null,bottom:null,left:null,right:null}}M.prototype=new L.jqplot.ElemContainer();M.prototype.constructor=M;M.prototype.init=function(){if(L.isFunction(this.renderer)){this.renderer=new this.renderer()}this.renderer.init.call(this,this.rendererOptions)};M.prototype.createElement=function(ah,ai){this._offsets=ah;return this.renderer.createElement.call(this,ai)};M.prototype.draw=function(){this.renderer.draw.call(this)};L.jqplot.GenericCanvas=function(){L.jqplot.ElemContainer.call(this);this._ctx};L.jqplot.GenericCanvas.prototype=new L.jqplot.ElemContainer();L.jqplot.GenericCanvas.prototype.constructor=L.jqplot.GenericCanvas;L.jqplot.GenericCanvas.prototype.createElement=function(al,aj,ai,am){this._offsets=al;var ah="jqplot";if(aj!=u){ah=aj}var ak;ak=am.canvasManager.getCanvas();if(ai!=null){this._plotDimensions=ai}ak.width=this._plotDimensions.width-this._offsets.left-this._offsets.right;ak.height=this._plotDimensions.height-this._offsets.top-this._offsets.bottom;this._elem=L(ak);this._elem.css({position:"absolute",left:this._offsets.left,top:this._offsets.top});this._elem.addClass(ah);ak=am.canvasManager.initCanvas(ak);ak=null;return this._elem};L.jqplot.GenericCanvas.prototype.setContext=function(){this._ctx=this._elem.get(0).getContext("2d");return this._ctx};L.jqplot.GenericCanvas.prototype.resetCanvas=function(){if(this._elem){if(L.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==u){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce()}this._ctx=null};L.jqplot.HooksManager=function(){this.hooks=[];this.args=[]};L.jqplot.HooksManager.prototype.addOnce=function(ak,ai){ai=ai||[];var al=false;for(var aj=0,ah=this.hooks.length;aj<ah;aj++){if(this.hooks[aj]==ak){al=true}}if(!al){this.hooks.push(ak);this.args.push(ai)}};L.jqplot.HooksManager.prototype.add=function(ai,ah){ah=ah||[];this.hooks.push(ai);this.args.push(ah)};L.jqplot.EventListenerManager=function(){this.hooks=[]};L.jqplot.EventListenerManager.prototype.addOnce=function(al,ak){var am=false,aj,ai;for(var ai=0,ah=this.hooks.length;ai<ah;ai++){aj=this.hooks[ai];if(aj[0]==al&&aj[1]==ak){am=true}}if(!am){this.hooks.push([al,ak])}};L.jqplot.EventListenerManager.prototype.add=function(ai,ah){this.hooks.push([ai,ah])};var U=["yMidAxis","xaxis","yaxis","x2axis","y2axis","y3axis","y4axis","y5axis","y6axis","y7axis","y8axis","y9axis"];function R(){this.animate=false;this.animateReplot=false;this.axes={xaxis:new w("xaxis"),yaxis:new w("yaxis"),x2axis:new w("x2axis"),y2axis:new w("y2axis"),y3axis:new w("y3axis"),y4axis:new w("y4axis"),y5axis:new w("y5axis"),y6axis:new w("y6axis"),y7axis:new w("y7axis"),y8axis:new w("y8axis"),y9axis:new w("y9axis"),yMidAxis:new w("yMidAxis")};this.baseCanvas=new L.jqplot.GenericCanvas();this.captureRightClick=false;this.data=[];this.dataRenderer;this.dataRendererOptions;this.defaults={axesDefaults:{},axes:{xaxis:{},yaxis:{},x2axis:{},y2axis:{},y3axis:{},y4axis:{},y5axis:{},y6axis:{},y7axis:{},y8axis:{},y9axis:{},yMidAxis:{}},seriesDefaults:{},series:[]};this.defaultAxisStart=1;this.drawIfHidden=false;this.eventCanvas=new L.jqplot.GenericCanvas();this.fillBetween={series1:null,series2:null,color:null,baseSeries:0,fill:true};this.fontFamily;this.fontSize;this.grid=new M();this.legend=new q();this.noDataIndicator={show:false,indicator:"Loading Data...",axes:{xaxis:{min:0,max:10,tickInterval:2,show:true},yaxis:{min:0,max:12,tickInterval:3,show:true}}};this.negativeSeriesColors=L.jqplot.config.defaultNegativeColors;this.options={};this.previousSeriesStack=[];this.plugins={};this.series=[];this.seriesStack=[];this.seriesColors=L.jqplot.config.defaultColors;this.sortData=true;this.stackSeries=false;this.syncXTicks=true;this.syncYTicks=true;this.target=null;this.targetId=null;this.textColor;this.title=new y();this._drawCount=0;this._sumy=0;this._sumx=0;this._stackData=[];this._plotData=[];this._width=null;this._height=null;this._plotDimensions={height:null,width:null};this._gridPadding={top:null,right:null,bottom:null,left:null};this._defaultGridPadding={top:10,right:10,bottom:23,left:10};this._addDomReference=L.jqplot.config.addDomReference;this.preInitHooks=new L.jqplot.HooksManager();this.postInitHooks=new L.jqplot.HooksManager();this.preParseOptionsHooks=new L.jqplot.HooksManager();this.postParseOptionsHooks=new L.jqplot.HooksManager();this.preDrawHooks=new L.jqplot.HooksManager();this.postDrawHooks=new L.jqplot.HooksManager();this.preDrawSeriesHooks=new L.jqplot.HooksManager();this.postDrawSeriesHooks=new L.jqplot.HooksManager();this.preDrawLegendHooks=new L.jqplot.HooksManager();this.addLegendRowHooks=new L.jqplot.HooksManager();this.preSeriesInitHooks=new L.jqplot.HooksManager();this.postSeriesInitHooks=new L.jqplot.HooksManager();this.preParseSeriesOptionsHooks=new L.jqplot.HooksManager();this.postParseSeriesOptionsHooks=new L.jqplot.HooksManager();this.eventListenerHooks=new L.jqplot.EventListenerManager();this.preDrawSeriesShadowHooks=new L.jqplot.HooksManager();this.postDrawSeriesShadowHooks=new L.jqplot.HooksManager();this.colorGenerator=new L.jqplot.ColorGenerator();this.negativeColorGenerator=new L.jqplot.ColorGenerator();this.canvasManager=new L.jqplot.CanvasManager();this.themeEngine=new L.jqplot.ThemeEngine();var aj=0;this.init=function(av,ar,ay){ay=ay||{};for(var at=0;at<L.jqplot.preInitHooks.length;at++){L.jqplot.preInitHooks[at].call(this,av,ar,ay)}for(var at=0;at<this.preInitHooks.hooks.length;at++){this.preInitHooks.hooks[at].call(this,av,ar,ay)}this.targetId="#"+av;this.target=L("#"+av);if(this._addDomReference){this.target.data("jqplot",this)}this.target.removeClass("jqplot-error");if(!this.target.get(0)){throw new Error("No plot target specified")}if(this.target.css("position")=="static"){this.target.css("position","relative")}if(!this.target.hasClass("jqplot-target")){this.target.addClass("jqplot-target")}if(!this.target.height()){var au;if(ay&&ay.height){au=parseInt(ay.height,10)}else{if(this.target.attr("data-height")){au=parseInt(this.target.attr("data-height"),10)}else{au=parseInt(L.jqplot.config.defaultHeight,10)}}this._height=au;this.target.css("height",au+"px")}else{this._height=au=this.target.height()}if(!this.target.width()){var aw;if(ay&&ay.width){aw=parseInt(ay.width,10)}else{if(this.target.attr("data-width")){aw=parseInt(this.target.attr("data-width"),10)}else{aw=parseInt(L.jqplot.config.defaultWidth,10)}}this._width=aw;this.target.css("width",aw+"px")}else{this._width=aw=this.target.width()}for(var at=0,ap=U.length;at<ap;at++){this.axes[U[at]]=new w(U[at])}this._plotDimensions.height=this._height;this._plotDimensions.width=this._width;this.grid._plotDimensions=this._plotDimensions;this.title._plotDimensions=this._plotDimensions;this.baseCanvas._plotDimensions=this._plotDimensions;this.eventCanvas._plotDimensions=this._plotDimensions;this.legend._plotDimensions=this._plotDimensions;if(this._height<=0||this._width<=0||!this._height||!this._width){throw new Error("Canvas dimension not set")}if(ay.dataRenderer&&L.isFunction(ay.dataRenderer)){if(ay.dataRendererOptions){this.dataRendererOptions=ay.dataRendererOptions}this.dataRenderer=ay.dataRenderer;ar=this.dataRenderer(ar,this,this.dataRendererOptions)}if(ay.noDataIndicator&&L.isPlainObject(ay.noDataIndicator)){L.extend(true,this.noDataIndicator,ay.noDataIndicator)}if(ar==null||L.isArray(ar)==false||ar.length==0||L.isArray(ar[0])==false||ar[0].length==0){if(this.noDataIndicator.show==false){throw new Error("No data specified")}else{for(var al in this.noDataIndicator.axes){for(var an in this.noDataIndicator.axes[al]){this.axes[al][an]=this.noDataIndicator.axes[al][an]}}this.postDrawHooks.add(function(){var aD=this.eventCanvas.getHeight();var aA=this.eventCanvas.getWidth();var az=L('<div class="jqplot-noData-container" style="position:absolute;"></div>');this.target.append(az);az.height(aD);az.width(aA);az.css("top",this.eventCanvas._offsets.top);az.css("left",this.eventCanvas._offsets.left);var aC=L('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>');az.append(aC);aC.html(this.noDataIndicator.indicator);var aB=aC.height();var ax=aC.width();aC.height(aB);aC.width(ax);aC.css("top",(aD-aB)/2+"px")})}}this.data=L.extend(true,[],ar);this.parseOptions(ay);if(this.textColor){this.target.css("color",this.textColor)}if(this.fontFamily){this.target.css("font-family",this.fontFamily)}if(this.fontSize){this.target.css("font-size",this.fontSize)}this.title.init();this.legend.init();this._sumy=0;this._sumx=0;this.computePlotData();for(var at=0;at<this.series.length;at++){this.seriesStack.push(at);this.previousSeriesStack.push(at);this.series[at].shadowCanvas._plotDimensions=this._plotDimensions;this.series[at].canvas._plotDimensions=this._plotDimensions;for(var aq=0;aq<L.jqplot.preSeriesInitHooks.length;aq++){L.jqplot.preSeriesInitHooks[aq].call(this.series[at],av,this.data,this.options.seriesDefaults,this.options.series[at],this)}for(var aq=0;aq<this.preSeriesInitHooks.hooks.length;aq++){this.preSeriesInitHooks.hooks[aq].call(this.series[at],av,this.data,this.options.seriesDefaults,this.options.series[at],this)}this.series[at]._plotDimensions=this._plotDimensions;this.series[at].init(at,this.grid.borderWidth,this);for(var aq=0;aq<L.jqplot.postSeriesInitHooks.length;aq++){L.jqplot.postSeriesInitHooks[aq].call(this.series[at],av,this.data,this.options.seriesDefaults,this.options.series[at],this)}for(var aq=0;aq<this.postSeriesInitHooks.hooks.length;aq++){this.postSeriesInitHooks.hooks[aq].call(this.series[at],av,this.data,this.options.seriesDefaults,this.options.series[at],this)}this._sumy+=this.series[at]._sumy;this._sumx+=this.series[at]._sumx}var am,ao;for(var at=0,ap=U.length;at<ap;at++){am=U[at];ao=this.axes[am];ao._plotDimensions=this._plotDimensions;ao.init();if(this.axes[am].borderColor==null){if(am.charAt(0)!=="x"&&ao.useSeriesColor===true&&ao.show){ao.borderColor=ao._series[0].color}else{ao.borderColor=this.grid.borderColor}}}if(this.sortData){ah(this.series)}this.grid.init();this.grid._axes=this.axes;this.legend._series=this.series;for(var at=0;at<L.jqplot.postInitHooks.length;at++){L.jqplot.postInitHooks[at].call(this,av,this.data,ay)}for(var at=0;at<this.postInitHooks.hooks.length;at++){this.postInitHooks.hooks[at].call(this,av,this.data,ay)}};this.resetAxesScale=function(aq,am){var ao=am||{};var ap=aq||this.axes;if(ap===true){ap=this.axes}if(L.isArray(ap)){for(var an=0;an<ap.length;an++){this.axes[ap[an]].resetScale(ao[ap[an]])}}else{if(typeof(ap)==="object"){for(var al in ap){this.axes[al].resetScale(ao[al])}}}};this.reInitialize=function(au,al){var ay=L.extend(true,{},this.options,al);var aw=this.targetId.substr(1);var ar=(au==null)?this.data:au;for(var av=0;av<L.jqplot.preInitHooks.length;av++){L.jqplot.preInitHooks[av].call(this,aw,ar,ay)}for(var av=0;av<this.preInitHooks.hooks.length;av++){this.preInitHooks.hooks[av].call(this,aw,ar,ay)}this._height=this.target.height();this._width=this.target.width();if(this._height<=0||this._width<=0||!this._height||!this._width){throw new Error("Target dimension not set")}this._plotDimensions.height=this._height;this._plotDimensions.width=this._width;this.grid._plotDimensions=this._plotDimensions;this.title._plotDimensions=this._plotDimensions;this.baseCanvas._plotDimensions=this._plotDimensions;this.eventCanvas._plotDimensions=this._plotDimensions;this.legend._plotDimensions=this._plotDimensions;var am,ax,at,ao;for(var av=0,aq=U.length;av<aq;av++){am=U[av];ao=this.axes[am];ax=ao._ticks;for(var at=0,ap=ax.length;at<ap;at++){var an=ax[at]._elem;if(an){if(L.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==u){window.G_vmlCanvasManager.uninitElement(an.get(0))}an.emptyForce();an=null;ax._elem=null}}ax=null;delete ao.ticks;delete ao._ticks;this.axes[am]=new w(am);this.axes[am]._plotWidth=this._width;this.axes[am]._plotHeight=this._height}if(au){if(ay.dataRenderer&&L.isFunction(ay.dataRenderer)){if(ay.dataRendererOptions){this.dataRendererOptions=ay.dataRendererOptions}this.dataRenderer=ay.dataRenderer;au=this.dataRenderer(au,this,this.dataRendererOptions)}this.data=L.extend(true,[],au)}if(al){this.parseOptions(ay)}this.title._plotWidth=this._width;if(this.textColor){this.target.css("color",this.textColor)}if(this.fontFamily){this.target.css("font-family",this.fontFamily)}if(this.fontSize){this.target.css("font-size",this.fontSize)}this.title.init();this.legend.init();this._sumy=0;this._sumx=0;this.seriesStack=[];this.previousSeriesStack=[];this.computePlotData();for(var av=0,aq=this.series.length;av<aq;av++){this.seriesStack.push(av);this.previousSeriesStack.push(av);this.series[av].shadowCanvas._plotDimensions=this._plotDimensions;this.series[av].canvas._plotDimensions=this._plotDimensions;for(var at=0;at<L.jqplot.preSeriesInitHooks.length;at++){L.jqplot.preSeriesInitHooks[at].call(this.series[av],aw,this.data,this.options.seriesDefaults,this.options.series[av],this)}for(var at=0;at<this.preSeriesInitHooks.hooks.length;at++){this.preSeriesInitHooks.hooks[at].call(this.series[av],aw,this.data,this.options.seriesDefaults,this.options.series[av],this)}this.series[av]._plotDimensions=this._plotDimensions;this.series[av].init(av,this.grid.borderWidth,this);for(var at=0;at<L.jqplot.postSeriesInitHooks.length;at++){L.jqplot.postSeriesInitHooks[at].call(this.series[av],aw,this.data,this.options.seriesDefaults,this.options.series[av],this)}for(var at=0;at<this.postSeriesInitHooks.hooks.length;at++){this.postSeriesInitHooks.hooks[at].call(this.series[av],aw,this.data,this.options.seriesDefaults,this.options.series[av],this)}this._sumy+=this.series[av]._sumy;this._sumx+=this.series[av]._sumx}for(var av=0,aq=U.length;av<aq;av++){am=U[av];ao=this.axes[am];ao._plotDimensions=this._plotDimensions;ao.init();if(ao.borderColor==null){if(am.charAt(0)!=="x"&&ao.useSeriesColor===true&&ao.show){ao.borderColor=ao._series[0].color}else{ao.borderColor=this.grid.borderColor}}}if(this.sortData){ah(this.series)}this.grid.init();this.grid._axes=this.axes;this.legend._series=this.series;for(var av=0,aq=L.jqplot.postInitHooks.length;av<aq;av++){L.jqplot.postInitHooks[av].call(this,aw,this.data,ay)}for(var av=0,aq=this.postInitHooks.hooks.length;av<aq;av++){this.postInitHooks.hooks[av].call(this,aw,this.data,ay)}};this.quickInit=function(){this._height=this.target.height();this._width=this.target.width();if(this._height<=0||this._width<=0||!this._height||!this._width){throw new Error("Target dimension not set")}this._plotDimensions.height=this._height;this._plotDimensions.width=this._width;this.grid._plotDimensions=this._plotDimensions;this.title._plotDimensions=this._plotDimensions;this.baseCanvas._plotDimensions=this._plotDimensions;this.eventCanvas._plotDimensions=this._plotDimensions;this.legend._plotDimensions=this._plotDimensions;for(var aq in this.axes){this.axes[aq]._plotWidth=this._width;this.axes[aq]._plotHeight=this._height}this.title._plotWidth=this._width;if(this.textColor){this.target.css("color",this.textColor)}if(this.fontFamily){this.target.css("font-family",this.fontFamily)}if(this.fontSize){this.target.css("font-size",this.fontSize)}this._sumy=0;this._sumx=0;this.computePlotData();for(var ao=0;ao<this.series.length;ao++){if(this.series[ao]._type==="line"&&this.series[ao].renderer.bands.show){this.series[ao].renderer.initBands.call(this.series[ao],this.series[ao].renderer.options,this)}this.series[ao]._plotDimensions=this._plotDimensions;this.series[ao].canvas._plotDimensions=this._plotDimensions;this._sumy+=this.series[ao]._sumy;this._sumx+=this.series[ao]._sumx}var am;for(var al=0;al<12;al++){am=U[al];var an=this.axes[am]._ticks;for(var ao=0;ao<an.length;ao++){var ap=an[ao]._elem;if(ap){if(L.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==u){window.G_vmlCanvasManager.uninitElement(ap.get(0))}ap.emptyForce();ap=null;an._elem=null}}an=null;this.axes[am]._plotDimensions=this._plotDimensions;this.axes[am]._ticks=[]}if(this.sortData){ah(this.series)}this.grid._axes=this.axes;this.legend._series=this.series};function ah(ap){var au,av,aw,al,at;for(var aq=0;aq<ap.length;aq++){var am;var ar=[ap[aq].data,ap[aq]._stackData,ap[aq]._plotData,ap[aq]._prevPlotData];for(var an=0;an<4;an++){am=true;au=ar[an];if(ap[aq]._stackAxis=="x"){for(var ao=0;ao<au.length;ao++){if(typeof(au[ao][1])!="number"){am=false;break}}if(am){au.sort(function(ay,ax){return ay[1]-ax[1]})}}else{for(var ao=0;ao<au.length;ao++){if(typeof(au[ao][0])!="number"){am=false;break}}if(am){au.sort(function(ay,ax){return ay[0]-ax[0]})}}}}}this.computePlotData=function(){this._plotData=[];this._stackData=[];var at,au,ao;for(au=0,ao=this.series.length;au<ao;au++){at=this.series[au];this._plotData.push([]);this._stackData.push([]);var am=at.data;this._plotData[au]=L.extend(true,[],am);this._stackData[au]=L.extend(true,[],am);at._plotData=this._plotData[au];at._stackData=this._stackData[au];var ax={x:[],y:[]};if(this.stackSeries&&!at.disableStack){at._stack=true;var av=(at._stackAxis==="x")?0:1;for(var ap=0,al=am.length;ap<al;ap++){var aw=am[ap][av];if(aw==null){aw=0}this._plotData[au][ap][av]=aw;this._stackData[au][ap][av]=aw;if(au>0){for(var aq=au;aq--;){var an=this._plotData[aq][ap][av];if(aw*an>=0){this._plotData[au][ap][av]+=an;this._stackData[au][ap][av]+=an;break}}}}}else{for(var ar=0;ar<at.data.length;ar++){ax.x.push(at.data[ar][0]);ax.y.push(at.data[ar][1])}this._stackData.push(at.data);this.series[au]._stackData=at.data;this._plotData.push(at.data);at._plotData=at.data;at._plotValues=ax}if(au>0){at._prevPlotData=this.series[au-1]._plotData}at._sumy=0;at._sumx=0;for(ar=at.data.length-1;ar>-1;ar--){at._sumy+=at.data[ar][1];at._sumx+=at.data[ar][0]}}};this.populatePlotData=function(au,av){this._plotData=[];this._stackData=[];au._stackData=[];au._plotData=[];var ay={x:[],y:[]};if(this.stackSeries&&!au.disableStack){au._stack=true;var ax=(au._stackAxis==="x")?0:1;var az=L.extend(true,[],au.data);var aA=L.extend(true,[],au.data);var an,am,ao,aw,al;for(var ar=0;ar<av;ar++){var ap=this.series[ar].data;for(var aq=0;aq<ap.length;aq++){ao=ap[aq];an=(ao[0]!=null)?ao[0]:0;am=(ao[1]!=null)?ao[1]:0;az[aq][0]+=an;az[aq][1]+=am;aw=(ax)?am:an;if(au.data[aq][ax]*aw>=0){aA[aq][ax]+=aw}}}for(var at=0;at<aA.length;at++){ay.x.push(aA[at][0]);ay.y.push(aA[at][1])}this._plotData.push(aA);this._stackData.push(az);au._stackData=az;au._plotData=aA;au._plotValues=ay}else{for(var at=0;at<au.data.length;at++){ay.x.push(au.data[at][0]);ay.y.push(au.data[at][1])}this._stackData.push(au.data);this.series[av]._stackData=au.data;this._plotData.push(au.data);au._plotData=au.data;au._plotValues=ay}if(av>0){au._prevPlotData=this.series[av-1]._plotData}au._sumy=0;au._sumx=0;for(at=au.data.length-1;at>-1;at--){au._sumy+=au.data[at][1];au._sumx+=au.data[at][0]}};this.getNextSeriesColor=(function(am){var al=0;var an=am.seriesColors;return function(){if(al<an.length){return an[al++]}else{al=0;return an[al++]}}})(this);this.parseOptions=function(ay){for(var at=0;at<this.preParseOptionsHooks.hooks.length;at++){this.preParseOptionsHooks.hooks[at].call(this,ay)}for(var at=0;at<L.jqplot.preParseOptionsHooks.length;at++){L.jqplot.preParseOptionsHooks[at].call(this,ay)}this.options=L.extend(true,{},this.defaults,ay);var am=this.options;this.animate=am.animate;this.animateReplot=am.animateReplot;this.stackSeries=am.stackSeries;if(L.isPlainObject(am.fillBetween)){var ax=["series1","series2","color","baseSeries","fill"],au;for(var at=0,aq=ax.length;at<aq;at++){au=ax[at];if(am.fillBetween[au]!=null){this.fillBetween[au]=am.fillBetween[au]}}}if(am.seriesColors){this.seriesColors=am.seriesColors}if(am.negativeSeriesColors){this.negativeSeriesColors=am.negativeSeriesColors}if(am.captureRightClick){this.captureRightClick=am.captureRightClick}this.defaultAxisStart=(ay&&ay.defaultAxisStart!=null)?ay.defaultAxisStart:this.defaultAxisStart;this.colorGenerator.setColors(this.seriesColors);this.negativeColorGenerator.setColors(this.negativeSeriesColors);L.extend(true,this._gridPadding,am.gridPadding);this.sortData=(am.sortData!=null)?am.sortData:this.sortData;for(var at=0;at<12;at++){var an=U[at];var ap=this.axes[an];ap._options=L.extend(true,{},am.axesDefaults,am.axes[an]);L.extend(true,ap,am.axesDefaults,am.axes[an]);ap._plotWidth=this._width;ap._plotHeight=this._height}var aw=function(aD,aB,aE){var aA=[];var aC,az;aB=aB||"vertical";if(!L.isArray(aD[0])){for(aC=0,az=aD.length;aC<az;aC++){if(aB=="vertical"){aA.push([aE+aC,aD[aC]])}else{aA.push([aD[aC],aE+aC])}}}else{L.extend(true,aA,aD)}return aA};var av=0;this.series=[];for(var at=0;at<this.data.length;at++){var al=L.extend(true,{index:at},{seriesColors:this.seriesColors,negativeSeriesColors:this.negativeSeriesColors},this.options.seriesDefaults,this.options.series[at],{rendererOptions:{animation:{show:this.animate}}});var ax=new S(al);for(var ar=0;ar<L.jqplot.preParseSeriesOptionsHooks.length;ar++){L.jqplot.preParseSeriesOptionsHooks[ar].call(ax,this.options.seriesDefaults,this.options.series[at])}for(var ar=0;ar<this.preParseSeriesOptionsHooks.hooks.length;ar++){this.preParseSeriesOptionsHooks.hooks[ar].call(ax,this.options.seriesDefaults,this.options.series[at])}L.extend(true,ax,al);var ao="vertical";if(ax.renderer===L.jqplot.BarRenderer&&ax.rendererOptions&&ax.rendererOptions.barDirection=="horizontal"){ao="horizontal";ax._stackAxis="x";ax._primaryAxis="_yaxis"}ax.data=aw(this.data[at],ao,this.defaultAxisStart);switch(ax.xaxis){case"xaxis":ax._xaxis=this.axes.xaxis;break;case"x2axis":ax._xaxis=this.axes.x2axis;break;default:break}ax._yaxis=this.axes[ax.yaxis];ax._xaxis._series.push(ax);ax._yaxis._series.push(ax);if(ax.show){ax._xaxis.show=true;ax._yaxis.show=true}else{if(ax._xaxis.scaleToHiddenSeries){ax._xaxis.show=true}if(ax._yaxis.scaleToHiddenSeries){ax._yaxis.show=true}}if(!ax.label){ax.label="Series "+(at+1).toString()}this.series.push(ax);for(var ar=0;ar<L.jqplot.postParseSeriesOptionsHooks.length;ar++){L.jqplot.postParseSeriesOptionsHooks[ar].call(this.series[at],this.options.seriesDefaults,this.options.series[at])}for(var ar=0;ar<this.postParseSeriesOptionsHooks.hooks.length;ar++){this.postParseSeriesOptionsHooks.hooks[ar].call(this.series[at],this.options.seriesDefaults,this.options.series[at])}}L.extend(true,this.grid,this.options.grid);for(var at=0,aq=U.length;at<aq;at++){var an=U[at];var ap=this.axes[an];if(ap.borderWidth==null){ap.borderWidth=this.grid.borderWidth}}if(typeof this.options.title=="string"){this.title.text=this.options.title}else{if(typeof this.options.title=="object"){L.extend(true,this.title,this.options.title)}}this.title._plotWidth=this._width;this.legend.setOptions(this.options.legend);for(var at=0;at<L.jqplot.postParseOptionsHooks.length;at++){L.jqplot.postParseOptionsHooks[at].call(this,ay)}for(var at=0;at<this.postParseOptionsHooks.hooks.length;at++){this.postParseOptionsHooks.hooks[at].call(this,ay)}};this.destroy=function(){this.canvasManager.freeAllCanvases();if(this.eventCanvas&&this.eventCanvas._elem){this.eventCanvas._elem.unbind()}this.target.empty();this.target[0].innerHTML=""};this.replot=function(am){var an=am||{};var ap=an.data||null;var al=(an.clear===false)?false:true;var ao=an.resetAxes||false;delete an.data;delete an.clear;delete an.resetAxes;this.target.trigger("jqplotPreReplot");if(al){this.destroy()}if(ap||!L.isEmptyObject(an)){this.reInitialize(ap,an)}else{this.quickInit()}if(ao){this.resetAxesScale(ao,an.axes)}this.draw();this.target.trigger("jqplotPostReplot")};this.redraw=function(al){al=(al!=null)?al:true;this.target.trigger("jqplotPreRedraw");if(al){this.canvasManager.freeAllCanvases();this.eventCanvas._elem.unbind();this.target.empty()}for(var an in this.axes){this.axes[an]._ticks=[]}this.computePlotData();this._sumy=0;this._sumx=0;for(var am=0,ao=this.series.length;am<ao;am++){this._sumy+=this.series[am]._sumy;this._sumx+=this.series[am]._sumx}this.draw();this.target.trigger("jqplotPostRedraw")};this.draw=function(){if(this.drawIfHidden||this.target.is(":visible")){this.target.trigger("jqplotPreDraw");var aH,aF,aE,ao;for(aH=0,aE=L.jqplot.preDrawHooks.length;aH<aE;aH++){L.jqplot.preDrawHooks[aH].call(this)}for(aH=0,aE=this.preDrawHooks.hooks.length;aH<aE;aH++){this.preDrawHooks.hooks[aH].apply(this,this.preDrawSeriesHooks.args[aH])}this.target.append(this.baseCanvas.createElement({left:0,right:0,top:0,bottom:0},"jqplot-base-canvas",null,this));this.baseCanvas.setContext();this.target.append(this.title.draw());this.title.pack({top:0,left:0});var aL=this.legend.draw({},this);var al={top:0,left:0,bottom:0,right:0};if(this.legend.placement=="outsideGrid"){this.target.append(aL);switch(this.legend.location){case"n":al.top+=this.legend.getHeight();break;case"s":al.bottom+=this.legend.getHeight();break;case"ne":case"e":case"se":al.right+=this.legend.getWidth();break;case"nw":case"w":case"sw":al.left+=this.legend.getWidth();break;default:al.right+=this.legend.getWidth();break}aL=aL.detach()}var ar=this.axes;var aM;for(aH=0;aH<12;aH++){aM=U[aH];this.target.append(ar[aM].draw(this.baseCanvas._ctx,this));ar[aM].set()}if(ar.yaxis.show){al.left+=ar.yaxis.getWidth()}var aG=["y2axis","y3axis","y4axis","y5axis","y6axis","y7axis","y8axis","y9axis"];var az=[0,0,0,0,0,0,0,0];var aC=0;var aB;for(aB=0;aB<8;aB++){if(ar[aG[aB]].show){aC+=ar[aG[aB]].getWidth();az[aB]=aC}}al.right+=aC;if(ar.x2axis.show){al.top+=ar.x2axis.getHeight()}if(this.title.show){al.top+=this.title.getHeight()}if(ar.xaxis.show){al.bottom+=ar.xaxis.getHeight()}if(this.options.gridDimensions&&L.isPlainObject(this.options.gridDimensions)){var at=parseInt(this.options.gridDimensions.width,10)||0;var aI=parseInt(this.options.gridDimensions.height,10)||0;var an=(this._width-al.left-al.right-at)/2;var aK=(this._height-al.top-al.bottom-aI)/2;if(aK>=0&&an>=0){al.top+=aK;al.bottom+=aK;al.left+=an;al.right+=an}}var am=["top","bottom","left","right"];for(var aB in am){if(this._gridPadding[am[aB]]==null&&al[am[aB]]>0){this._gridPadding[am[aB]]=al[am[aB]]}else{if(this._gridPadding[am[aB]]==null){this._gridPadding[am[aB]]=this._defaultGridPadding[am[aB]]}}}var aA=this._gridPadding;if(this.legend.placement==="outsideGrid"){aA={top:this.title.getHeight(),left:0,right:0,bottom:0};if(this.legend.location==="s"){aA.left=this._gridPadding.left;aA.right=this._gridPadding.right}}ar.xaxis.pack({position:"absolute",bottom:this._gridPadding.bottom-ar.xaxis.getHeight(),left:0,width:this._width},{min:this._gridPadding.left,max:this._width-this._gridPadding.right});ar.yaxis.pack({position:"absolute",top:0,left:this._gridPadding.left-ar.yaxis.getWidth(),height:this._height},{min:this._height-this._gridPadding.bottom,max:this._gridPadding.top});ar.x2axis.pack({position:"absolute",top:this._gridPadding.top-ar.x2axis.getHeight(),left:0,width:this._width},{min:this._gridPadding.left,max:this._width-this._gridPadding.right});for(aH=8;aH>0;aH--){ar[aG[aH-1]].pack({position:"absolute",top:0,right:this._gridPadding.right-az[aH-1]},{min:this._height-this._gridPadding.bottom,max:this._gridPadding.top})}var au=(this._width-this._gridPadding.left-this._gridPadding.right)/2+this._gridPadding.left-ar.yMidAxis.getWidth()/2;ar.yMidAxis.pack({position:"absolute",top:0,left:au,zIndex:9,textAlign:"center"},{min:this._height-this._gridPadding.bottom,max:this._gridPadding.top});this.target.append(this.grid.createElement(this._gridPadding,this));this.grid.draw();var aq=this.series;var aJ=aq.length;for(aH=0,aE=aJ;aH<aE;aH++){aF=this.seriesStack[aH];this.target.append(aq[aF].shadowCanvas.createElement(this._gridPadding,"jqplot-series-shadowCanvas",null,this));aq[aF].shadowCanvas.setContext();aq[aF].shadowCanvas._elem.data("seriesIndex",aF)}for(aH=0,aE=aJ;aH<aE;aH++){aF=this.seriesStack[aH];this.target.append(aq[aF].canvas.createElement(this._gridPadding,"jqplot-series-canvas",null,this));aq[aF].canvas.setContext();aq[aF].canvas._elem.data("seriesIndex",aF)}this.target.append(this.eventCanvas.createElement(this._gridPadding,"jqplot-event-canvas",null,this));this.eventCanvas.setContext();this.eventCanvas._ctx.fillStyle="rgba(0,0,0,0)";this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width,this.eventCanvas._ctx.canvas.height);this.bindCustomEvents();if(this.legend.preDraw){this.eventCanvas._elem.before(aL);this.legend.pack(aA);if(this.legend._elem){this.drawSeries({legendInfo:{location:this.legend.location,placement:this.legend.placement,width:this.legend.getWidth(),height:this.legend.getHeight(),xoffset:this.legend.xoffset,yoffset:this.legend.yoffset}})}else{this.drawSeries()}}else{this.drawSeries();if(aJ){L(aq[aJ-1].canvas._elem).after(aL)}this.legend.pack(aA)}for(var aH=0,aE=L.jqplot.eventListenerHooks.length;aH<aE;aH++){this.eventCanvas._elem.bind(L.jqplot.eventListenerHooks[aH][0],{plot:this},L.jqplot.eventListenerHooks[aH][1])}for(var aH=0,aE=this.eventListenerHooks.hooks.length;aH<aE;aH++){this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[aH][0],{plot:this},this.eventListenerHooks.hooks[aH][1])}var ay=this.fillBetween;if(ay.fill&&ay.series1!==ay.series2&&ay.series1<aJ&&ay.series2<aJ&&aq[ay.series1]._type==="line"&&aq[ay.series2]._type==="line"){this.doFillBetweenLines()}for(var aH=0,aE=L.jqplot.postDrawHooks.length;aH<aE;aH++){L.jqplot.postDrawHooks[aH].call(this)}for(var aH=0,aE=this.postDrawHooks.hooks.length;aH<aE;aH++){this.postDrawHooks.hooks[aH].apply(this,this.postDrawHooks.args[aH])}if(this.target.is(":visible")){this._drawCount+=1}var av,aw,aD,ap;for(aH=0,aE=aJ;aH<aE;aH++){av=aq[aH];aw=av.renderer;aD=".jqplot-point-label.jqplot-series-"+aH;if(aw.animation&&aw.animation._supported&&aw.animation.show&&(this._drawCount<2||this.animateReplot)){ap=this.target.find(aD);ap.stop(true,true).hide();av.canvas._elem.stop(true,true).hide();av.shadowCanvas._elem.stop(true,true).hide();av.canvas._elem.jqplotEffect("blind",{mode:"show",direction:aw.animation.direction},aw.animation.speed);av.shadowCanvas._elem.jqplotEffect("blind",{mode:"show",direction:aw.animation.direction},aw.animation.speed);ap.fadeIn(aw.animation.speed*0.8)}}ap=null;this.target.trigger("jqplotPostDraw",[this])}};R.prototype.doFillBetweenLines=function(){var an=this.fillBetween;var ax=an.series1;var av=an.series2;var aw=(ax<av)?ax:av;var au=(av>ax)?av:ax;var ar=this.series[aw];var aq=this.series[au];if(aq.renderer.smooth){var ap=aq.renderer._smoothedData.slice(0).reverse()}else{var ap=aq.gridData.slice(0).reverse()}if(ar.renderer.smooth){var at=ar.renderer._smoothedData.concat(ap)}else{var at=ar.gridData.concat(ap)}var ao=(an.color!==null)?an.color:this.series[ax].fillColor;var ay=(an.baseSeries!==null)?an.baseSeries:aw;var am=this.series[ay].renderer.shapeRenderer;var al={fillStyle:ao,fill:true,closePath:true};am.draw(ar.shadowCanvas._ctx,at,al)};this.bindCustomEvents=function(){this.eventCanvas._elem.bind("click",{plot:this},this.onClick);this.eventCanvas._elem.bind("dblclick",{plot:this},this.onDblClick);this.eventCanvas._elem.bind("mousedown",{plot:this},this.onMouseDown);this.eventCanvas._elem.bind("mousemove",{plot:this},this.onMouseMove);this.eventCanvas._elem.bind("mouseenter",{plot:this},this.onMouseEnter);this.eventCanvas._elem.bind("mouseleave",{plot:this},this.onMouseLeave);if(this.captureRightClick){this.eventCanvas._elem.bind("mouseup",{plot:this},this.onRightClick);this.eventCanvas._elem.get(0).oncontextmenu=function(){return false}}else{this.eventCanvas._elem.bind("mouseup",{plot:this},this.onMouseUp)}};function ai(av){var au=av.data.plot;var ap=au.eventCanvas._elem.offset();var at={x:av.pageX-ap.left,y:av.pageY-ap.top};var aq={xaxis:null,yaxis:null,x2axis:null,y2axis:null,y3axis:null,y4axis:null,y5axis:null,y6axis:null,y7axis:null,y8axis:null,y9axis:null,yMidAxis:null};var ar=["xaxis","yaxis","x2axis","y2axis","y3axis","y4axis","y5axis","y6axis","y7axis","y8axis","y9axis","yMidAxis"];var al=au.axes;var am,ao;for(am=11;am>0;am--){ao=ar[am-1];if(al[ao].show){aq[ao]=al[ao].series_p2u(at[ao.charAt(0)])}}return{offsets:ap,gridPos:at,dataPos:aq}}function ak(al,am){var aq=am.series;var aW,aU,aT,aO,aP,aJ,aI,aw,au,az,aA,aK;var aS,aX,aQ,ar,aH,aM,aV;var an,aN;for(aT=am.seriesStack.length-1;aT>=0;aT--){aW=am.seriesStack[aT];aO=aq[aW];aV=aO._highlightThreshold;switch(aO.renderer.constructor){case L.jqplot.BarRenderer:aJ=al.x;aI=al.y;for(aU=0;aU<aO._barPoints.length;aU++){aH=aO._barPoints[aU];aQ=aO.gridData[aU];if(aJ>aH[0][0]&&aJ<aH[2][0]&&aI>aH[2][1]&&aI<aH[0][1]){return{seriesIndex:aO.index,pointIndex:aU,gridData:aQ,data:aO.data[aU],points:aO._barPoints[aU]}}}break;case L.jqplot.PyramidRenderer:aJ=al.x;aI=al.y;for(aU=0;aU<aO._barPoints.length;aU++){aH=aO._barPoints[aU];aQ=aO.gridData[aU];if(aJ>aH[0][0]+aV[0][0]&&aJ<aH[2][0]+aV[2][0]&&aI>aH[2][1]&&aI<aH[0][1]){return{seriesIndex:aO.index,pointIndex:aU,gridData:aQ,data:aO.data[aU],points:aO._barPoints[aU]}}}break;case L.jqplot.DonutRenderer:az=aO.startAngle/180*Math.PI;aJ=al.x-aO._center[0];aI=al.y-aO._center[1];aP=Math.sqrt(Math.pow(aJ,2)+Math.pow(aI,2));if(aJ>0&&-aI>=0){aw=2*Math.PI-Math.atan(-aI/aJ)}else{if(aJ>0&&-aI<0){aw=-Math.atan(-aI/aJ)}else{if(aJ<0){aw=Math.PI-Math.atan(-aI/aJ)}else{if(aJ==0&&-aI>0){aw=3*Math.PI/2}else{if(aJ==0&&-aI<0){aw=Math.PI/2}else{if(aJ==0&&aI==0){aw=0}}}}}}if(az){aw-=az;if(aw<0){aw+=2*Math.PI}else{if(aw>2*Math.PI){aw-=2*Math.PI}}}au=aO.sliceMargin/180*Math.PI;if(aP<aO._radius&&aP>aO._innerRadius){for(aU=0;aU<aO.gridData.length;aU++){aA=(aU>0)?aO.gridData[aU-1][1]+au:au;aK=aO.gridData[aU][1];if(aw>aA&&aw<aK){return{seriesIndex:aO.index,pointIndex:aU,gridData:[al.x,al.y],data:aO.data[aU]}}}}break;case L.jqplot.PieRenderer:az=aO.startAngle/180*Math.PI;aJ=al.x-aO._center[0];aI=al.y-aO._center[1];aP=Math.sqrt(Math.pow(aJ,2)+Math.pow(aI,2));if(aJ>0&&-aI>=0){aw=2*Math.PI-Math.atan(-aI/aJ)}else{if(aJ>0&&-aI<0){aw=-Math.atan(-aI/aJ)}else{if(aJ<0){aw=Math.PI-Math.atan(-aI/aJ)}else{if(aJ==0&&-aI>0){aw=3*Math.PI/2}else{if(aJ==0&&-aI<0){aw=Math.PI/2}else{if(aJ==0&&aI==0){aw=0}}}}}}if(az){aw-=az;if(aw<0){aw+=2*Math.PI}else{if(aw>2*Math.PI){aw-=2*Math.PI}}}au=aO.sliceMargin/180*Math.PI;if(aP<aO._radius){for(aU=0;aU<aO.gridData.length;aU++){aA=(aU>0)?aO.gridData[aU-1][1]+au:au;aK=aO.gridData[aU][1];if(aw>aA&&aw<aK){return{seriesIndex:aO.index,pointIndex:aU,gridData:[al.x,al.y],data:aO.data[aU]}}}}break;case L.jqplot.BubbleRenderer:aJ=al.x;aI=al.y;var aF=null;if(aO.show){for(var aU=0;aU<aO.gridData.length;aU++){aQ=aO.gridData[aU];aX=Math.sqrt((aJ-aQ[0])*(aJ-aQ[0])+(aI-aQ[1])*(aI-aQ[1]));if(aX<=aQ[2]&&(aX<=aS||aS==null)){aS=aX;aF={seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}if(aF!=null){return aF}}break;case L.jqplot.FunnelRenderer:aJ=al.x;aI=al.y;var aL=aO._vertices,ap=aL[0],ao=aL[aL.length-1],at,aE,ay;function aR(a0,a2,a1){var aZ=(a2[1]-a1[1])/(a2[0]-a1[0]);var aY=a2[1]-aZ*a2[0];var a3=a0+a2[1];return[(a3-aY)/aZ,a3]}at=aR(aI,ap[0],ao[3]);aE=aR(aI,ap[1],ao[2]);for(aU=0;aU<aL.length;aU++){ay=aL[aU];if(aI>=ay[0][1]&&aI<=ay[3][1]&&aJ>=at[0]&&aJ<=aE[0]){return{seriesIndex:aO.index,pointIndex:aU,gridData:null,data:aO.data[aU]}}}break;case L.jqplot.LineRenderer:aJ=al.x;aI=al.y;aP=aO.renderer;if(aO.show){if((aO.fill||(aO.renderer.bands.show&&aO.renderer.bands.fill))&&(!am.plugins.highlighter||!am.plugins.highlighter.show)){var ax=false;if(aJ>aO._boundingBox[0][0]&&aJ<aO._boundingBox[1][0]&&aI>aO._boundingBox[1][1]&&aI<aO._boundingBox[0][1]){var aD=aO._areaPoints.length;var aG;var aU=aD-1;for(var aG=0;aG<aD;aG++){var aC=[aO._areaPoints[aG][0],aO._areaPoints[aG][1]];var aB=[aO._areaPoints[aU][0],aO._areaPoints[aU][1]];if(aC[1]<aI&&aB[1]>=aI||aB[1]<aI&&aC[1]>=aI){if(aC[0]+(aI-aC[1])/(aB[1]-aC[1])*(aB[0]-aC[0])<aJ){ax=!ax}}aU=aG}}if(ax){return{seriesIndex:aW,pointIndex:null,gridData:aO.gridData,data:aO.data,points:aO._areaPoints}}break}else{aN=aO.markerRenderer.size/2+aO.neighborThreshold;an=(aN>0)?aN:0;for(var aU=0;aU<aO.gridData.length;aU++){aQ=aO.gridData[aU];if(aP.constructor==L.jqplot.OHLCRenderer){if(aP.candleStick){var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._bodyWidth/2&&aJ<=aQ[0]+aP._bodyWidth/2&&aI>=av(aO.data[aU][2])&&aI<=av(aO.data[aU][3])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}else{if(!aP.hlc){var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._tickLength&&aJ<=aQ[0]+aP._tickLength&&aI>=av(aO.data[aU][2])&&aI<=av(aO.data[aU][3])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}else{var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._tickLength&&aJ<=aQ[0]+aP._tickLength&&aI>=av(aO.data[aU][1])&&aI<=av(aO.data[aU][2])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}}}else{if(aQ[0]!=null&&aQ[1]!=null){aX=Math.sqrt((aJ-aQ[0])*(aJ-aQ[0])+(aI-aQ[1])*(aI-aQ[1]));if(aX<=an&&(aX<=aS||aS==null)){aS=aX;return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}}}}}break;default:aJ=al.x;aI=al.y;aP=aO.renderer;if(aO.show){aN=aO.markerRenderer.size/2+aO.neighborThreshold;an=(aN>0)?aN:0;for(var aU=0;aU<aO.gridData.length;aU++){aQ=aO.gridData[aU];if(aP.constructor==L.jqplot.OHLCRenderer){if(aP.candleStick){var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._bodyWidth/2&&aJ<=aQ[0]+aP._bodyWidth/2&&aI>=av(aO.data[aU][2])&&aI<=av(aO.data[aU][3])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}else{if(!aP.hlc){var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._tickLength&&aJ<=aQ[0]+aP._tickLength&&aI>=av(aO.data[aU][2])&&aI<=av(aO.data[aU][3])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}else{var av=aO._yaxis.series_u2p;if(aJ>=aQ[0]-aP._tickLength&&aJ<=aQ[0]+aP._tickLength&&aI>=av(aO.data[aU][1])&&aI<=av(aO.data[aU][2])){return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}}}else{aX=Math.sqrt((aJ-aQ[0])*(aJ-aQ[0])+(aI-aQ[1])*(aI-aQ[1]));if(aX<=an&&(aX<=aS||aS==null)){aS=aX;return{seriesIndex:aW,pointIndex:aU,gridData:aQ,data:aO.data[aU]}}}}}break}}return null}this.onClick=function(an){var am=ai(an);var ap=an.data.plot;var ao=ak(am.gridPos,ap);var al=L.Event("jqplotClick");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])};this.onDblClick=function(an){var am=ai(an);var ap=an.data.plot;var ao=ak(am.gridPos,ap);var al=L.Event("jqplotDblClick");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])};this.onMouseDown=function(an){var am=ai(an);var ap=an.data.plot;var ao=ak(am.gridPos,ap);var al=L.Event("jqplotMouseDown");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])};this.onMouseUp=function(an){var am=ai(an);var al=L.Event("jqplotMouseUp");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,null,an.data.plot])};this.onRightClick=function(an){var am=ai(an);var ap=an.data.plot;var ao=ak(am.gridPos,ap);if(ap.captureRightClick){if(an.which==3){var al=L.Event("jqplotRightClick");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])}else{var al=L.Event("jqplotMouseUp");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])}}};this.onMouseMove=function(an){var am=ai(an);var ap=an.data.plot;var ao=ak(am.gridPos,ap);var al=L.Event("jqplotMouseMove");al.pageX=an.pageX;al.pageY=an.pageY;L(this).trigger(al,[am.gridPos,am.dataPos,ao,ap])};this.onMouseEnter=function(an){var am=ai(an);var ao=an.data.plot;var al=L.Event("jqplotMouseEnter");al.pageX=an.pageX;al.pageY=an.pageY;al.relatedTarget=an.relatedTarget;L(this).trigger(al,[am.gridPos,am.dataPos,null,ao])};this.onMouseLeave=function(an){var am=ai(an);var ao=an.data.plot;var al=L.Event("jqplotMouseLeave");al.pageX=an.pageX;al.pageY=an.pageY;al.relatedTarget=an.relatedTarget;L(this).trigger(al,[am.gridPos,am.dataPos,null,ao])};this.drawSeries=function(an,al){var ap,ao,am;al=(typeof(an)==="number"&&al==null)?an:al;an=(typeof(an)==="object")?an:{};if(al!=u){ao=this.series[al];am=ao.shadowCanvas._ctx;am.clearRect(0,0,am.canvas.width,am.canvas.height);ao.drawShadow(am,an,this);am=ao.canvas._ctx;am.clearRect(0,0,am.canvas.width,am.canvas.height);ao.draw(am,an,this);if(ao.renderer.constructor==L.jqplot.BezierCurveRenderer){if(al<this.series.length-1){this.drawSeries(al+1)}}}else{for(ap=0;ap<this.series.length;ap++){ao=this.series[ap];am=ao.shadowCanvas._ctx;am.clearRect(0,0,am.canvas.width,am.canvas.height);ao.drawShadow(am,an,this);am=ao.canvas._ctx;am.clearRect(0,0,am.canvas.width,am.canvas.height);ao.draw(am,an,this)}}an=al=ap=ao=am=null};this.moveSeriesToFront=function(am){am=parseInt(am,10);var ap=L.inArray(am,this.seriesStack);if(ap==-1){return}if(ap==this.seriesStack.length-1){this.previousSeriesStack=this.seriesStack.slice(0);return}var al=this.seriesStack[this.seriesStack.length-1];var ao=this.series[am].canvas._elem.detach();var an=this.series[am].shadowCanvas._elem.detach();this.series[al].shadowCanvas._elem.after(an);this.series[al].canvas._elem.after(ao);this.previousSeriesStack=this.seriesStack.slice(0);this.seriesStack.splice(ap,1);this.seriesStack.push(am)};this.moveSeriesToBack=function(am){am=parseInt(am,10);var ap=L.inArray(am,this.seriesStack);if(ap==0||ap==-1){return}var al=this.seriesStack[0];var ao=this.series[am].canvas._elem.detach();var an=this.series[am].shadowCanvas._elem.detach();this.series[al].shadowCanvas._elem.before(an);this.series[al].canvas._elem.before(ao);this.previousSeriesStack=this.seriesStack.slice(0);this.seriesStack.splice(ap,1);this.seriesStack.unshift(am)};this.restorePreviousSeriesOrder=function(){var ar,aq,ap,ao,an,al,am;if(this.seriesStack==this.previousSeriesStack){return}for(ar=1;ar<this.previousSeriesStack.length;ar++){al=this.previousSeriesStack[ar];am=this.previousSeriesStack[ar-1];ap=this.series[al].canvas._elem.detach();ao=this.series[al].shadowCanvas._elem.detach();this.series[am].shadowCanvas._elem.after(ao);this.series[am].canvas._elem.after(ap)}an=this.seriesStack.slice(0);this.seriesStack=this.previousSeriesStack.slice(0);this.previousSeriesStack=an};this.restoreOriginalSeriesOrder=function(){var ap,ao,al=[],an,am;for(ap=0;ap<this.series.length;ap++){al.push(ap)}if(this.seriesStack==al){return}this.previousSeriesStack=this.seriesStack.slice(0);this.seriesStack=al;for(ap=1;ap<this.seriesStack.length;ap++){an=this.series[ap].canvas._elem.detach();am=this.series[ap].shadowCanvas._elem.detach();this.series[ap-1].shadowCanvas._elem.after(am);this.series[ap-1].canvas._elem.after(an)}};this.activateTheme=function(al){this.themeEngine.activate(this,al)}}L.jqplot.computeHighlightColors=function(ai){var ak;if(L.isArray(ai)){ak=[];for(var am=0;am<ai.length;am++){var al=L.jqplot.getColorComponents(ai[am]);var ah=[al[0],al[1],al[2]];var an=ah[0]+ah[1]+ah[2];for(var aj=0;aj<3;aj++){ah[aj]=(an>660)?ah[aj]*0.85:0.73*ah[aj]+90;ah[aj]=parseInt(ah[aj],10);(ah[aj]>255)?255:ah[aj]}ah[3]=0.3+0.35*al[3];ak.push("rgba("+ah[0]+","+ah[1]+","+ah[2]+","+ah[3]+")")}}else{var al=L.jqplot.getColorComponents(ai);var ah=[al[0],al[1],al[2]];var an=ah[0]+ah[1]+ah[2];for(var aj=0;aj<3;aj++){ah[aj]=(an>660)?ah[aj]*0.85:0.73*ah[aj]+90;ah[aj]=parseInt(ah[aj],10);(ah[aj]>255)?255:ah[aj]}ah[3]=0.3+0.35*al[3];ak="rgba("+ah[0]+","+ah[1]+","+ah[2]+","+ah[3]+")"}return ak};L.jqplot.ColorGenerator=function(ai){ai=ai||L.jqplot.config.defaultColors;var ah=0;this.next=function(){if(ah<ai.length){return ai[ah++]}else{ah=0;return ai[ah++]}};this.previous=function(){if(ah>0){return ai[ah--]}else{ah=ai.length-1;return ai[ah]}};this.get=function(ak){var aj=ak-ai.length*Math.floor(ak/ai.length);return ai[aj]};this.setColors=function(aj){ai=aj};this.reset=function(){ah=0};this.getIndex=function(){return ah};this.setIndex=function(aj){ah=aj}};L.jqplot.hex2rgb=function(aj,ah){aj=aj.replace("#","");if(aj.length==3){aj=aj.charAt(0)+aj.charAt(0)+aj.charAt(1)+aj.charAt(1)+aj.charAt(2)+aj.charAt(2)}var ai;ai="rgba("+parseInt(aj.slice(0,2),16)+", "+parseInt(aj.slice(2,4),16)+", "+parseInt(aj.slice(4,6),16);if(ah){ai+=", "+ah}ai+=")";return ai};L.jqplot.rgb2hex=function(am){var aj=/rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/;var ah=am.match(aj);var al="#";for(var ak=1;ak<4;ak++){var ai;if(ah[ak].search(/%/)!=-1){ai=parseInt(255*ah[ak]/100,10).toString(16);if(ai.length==1){ai="0"+ai}}else{ai=parseInt(ah[ak],10).toString(16);if(ai.length==1){ai="0"+ai}}al+=ai}return al};L.jqplot.normalize2rgb=function(ai,ah){if(ai.search(/^ *rgba?\(/)!=-1){return ai}else{if(ai.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/)!=-1){return L.jqplot.hex2rgb(ai,ah)}else{throw new Error("Invalid color spec")}}};L.jqplot.getColorComponents=function(am){am=L.jqplot.colorKeywordMap[am]||am;var ak=L.jqplot.normalize2rgb(am);var aj=/rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/;var ah=ak.match(aj);var ai=[];for(var al=1;al<4;al++){if(ah[al].search(/%/)!=-1){ai[al-1]=parseInt(255*ah[al]/100,10)}else{ai[al-1]=parseInt(ah[al],10)}}ai[3]=parseFloat(ah[4])?parseFloat(ah[4]):1;return ai};L.jqplot.colorKeywordMap={aliceblue:"rgb(240, 248, 255)",antiquewhite:"rgb(250, 235, 215)",aqua:"rgb( 0, 255, 255)",aquamarine:"rgb(127, 255, 212)",azure:"rgb(240, 255, 255)",beige:"rgb(245, 245, 220)",bisque:"rgb(255, 228, 196)",black:"rgb( 0, 0, 0)",blanchedalmond:"rgb(255, 235, 205)",blue:"rgb( 0, 0, 255)",blueviolet:"rgb(138, 43, 226)",brown:"rgb(165, 42, 42)",burlywood:"rgb(222, 184, 135)",cadetblue:"rgb( 95, 158, 160)",chartreuse:"rgb(127, 255, 0)",chocolate:"rgb(210, 105, 30)",coral:"rgb(255, 127, 80)",cornflowerblue:"rgb(100, 149, 237)",cornsilk:"rgb(255, 248, 220)",crimson:"rgb(220, 20, 60)",cyan:"rgb( 0, 255, 255)",darkblue:"rgb( 0, 0, 139)",darkcyan:"rgb( 0, 139, 139)",darkgoldenrod:"rgb(184, 134, 11)",darkgray:"rgb(169, 169, 169)",darkgreen:"rgb( 0, 100, 0)",darkgrey:"rgb(169, 169, 169)",darkkhaki:"rgb(189, 183, 107)",darkmagenta:"rgb(139, 0, 139)",darkolivegreen:"rgb( 85, 107, 47)",darkorange:"rgb(255, 140, 0)",darkorchid:"rgb(153, 50, 204)",darkred:"rgb(139, 0, 0)",darksalmon:"rgb(233, 150, 122)",darkseagreen:"rgb(143, 188, 143)",darkslateblue:"rgb( 72, 61, 139)",darkslategray:"rgb( 47, 79, 79)",darkslategrey:"rgb( 47, 79, 79)",darkturquoise:"rgb( 0, 206, 209)",darkviolet:"rgb(148, 0, 211)",deeppink:"rgb(255, 20, 147)",deepskyblue:"rgb( 0, 191, 255)",dimgray:"rgb(105, 105, 105)",dimgrey:"rgb(105, 105, 105)",dodgerblue:"rgb( 30, 144, 255)",firebrick:"rgb(178, 34, 34)",floralwhite:"rgb(255, 250, 240)",forestgreen:"rgb( 34, 139, 34)",fuchsia:"rgb(255, 0, 255)",gainsboro:"rgb(220, 220, 220)",ghostwhite:"rgb(248, 248, 255)",gold:"rgb(255, 215, 0)",goldenrod:"rgb(218, 165, 32)",gray:"rgb(128, 128, 128)",grey:"rgb(128, 128, 128)",green:"rgb( 0, 128, 0)",greenyellow:"rgb(173, 255, 47)",honeydew:"rgb(240, 255, 240)",hotpink:"rgb(255, 105, 180)",indianred:"rgb(205, 92, 92)",indigo:"rgb( 75, 0, 130)",ivory:"rgb(255, 255, 240)",khaki:"rgb(240, 230, 140)",lavender:"rgb(230, 230, 250)",lavenderblush:"rgb(255, 240, 245)",lawngreen:"rgb(124, 252, 0)",lemonchiffon:"rgb(255, 250, 205)",lightblue:"rgb(173, 216, 230)",lightcoral:"rgb(240, 128, 128)",lightcyan:"rgb(224, 255, 255)",lightgoldenrodyellow:"rgb(250, 250, 210)",lightgray:"rgb(211, 211, 211)",lightgreen:"rgb(144, 238, 144)",lightgrey:"rgb(211, 211, 211)",lightpink:"rgb(255, 182, 193)",lightsalmon:"rgb(255, 160, 122)",lightseagreen:"rgb( 32, 178, 170)",lightskyblue:"rgb(135, 206, 250)",lightslategray:"rgb(119, 136, 153)",lightslategrey:"rgb(119, 136, 153)",lightsteelblue:"rgb(176, 196, 222)",lightyellow:"rgb(255, 255, 224)",lime:"rgb( 0, 255, 0)",limegreen:"rgb( 50, 205, 50)",linen:"rgb(250, 240, 230)",magenta:"rgb(255, 0, 255)",maroon:"rgb(128, 0, 0)",mediumaquamarine:"rgb(102, 205, 170)",mediumblue:"rgb( 0, 0, 205)",mediumorchid:"rgb(186, 85, 211)",mediumpurple:"rgb(147, 112, 219)",mediumseagreen:"rgb( 60, 179, 113)",mediumslateblue:"rgb(123, 104, 238)",mediumspringgreen:"rgb( 0, 250, 154)",mediumturquoise:"rgb( 72, 209, 204)",mediumvioletred:"rgb(199, 21, 133)",midnightblue:"rgb( 25, 25, 112)",mintcream:"rgb(245, 255, 250)",mistyrose:"rgb(255, 228, 225)",moccasin:"rgb(255, 228, 181)",navajowhite:"rgb(255, 222, 173)",navy:"rgb( 0, 0, 128)",oldlace:"rgb(253, 245, 230)",olive:"rgb(128, 128, 0)",olivedrab:"rgb(107, 142, 35)",orange:"rgb(255, 165, 0)",orangered:"rgb(255, 69, 0)",orchid:"rgb(218, 112, 214)",palegoldenrod:"rgb(238, 232, 170)",palegreen:"rgb(152, 251, 152)",paleturquoise:"rgb(175, 238, 238)",palevioletred:"rgb(219, 112, 147)",papayawhip:"rgb(255, 239, 213)",peachpuff:"rgb(255, 218, 185)",peru:"rgb(205, 133, 63)",pink:"rgb(255, 192, 203)",plum:"rgb(221, 160, 221)",powderblue:"rgb(176, 224, 230)",purple:"rgb(128, 0, 128)",red:"rgb(255, 0, 0)",rosybrown:"rgb(188, 143, 143)",royalblue:"rgb( 65, 105, 225)",saddlebrown:"rgb(139, 69, 19)",salmon:"rgb(250, 128, 114)",sandybrown:"rgb(244, 164, 96)",seagreen:"rgb( 46, 139, 87)",seashell:"rgb(255, 245, 238)",sienna:"rgb(160, 82, 45)",silver:"rgb(192, 192, 192)",skyblue:"rgb(135, 206, 235)",slateblue:"rgb(106, 90, 205)",slategray:"rgb(112, 128, 144)",slategrey:"rgb(112, 128, 144)",snow:"rgb(255, 250, 250)",springgreen:"rgb( 0, 255, 127)",steelblue:"rgb( 70, 130, 180)",tan:"rgb(210, 180, 140)",teal:"rgb( 0, 128, 128)",thistle:"rgb(216, 191, 216)",tomato:"rgb(255, 99, 71)",turquoise:"rgb( 64, 224, 208)",violet:"rgb(238, 130, 238)",wheat:"rgb(245, 222, 179)",white:"rgb(255, 255, 255)",whitesmoke:"rgb(245, 245, 245)",yellow:"rgb(255, 255, 0)",yellowgreen:"rgb(154, 205, 50)"};L.jqplot.AxisLabelRenderer=function(ah){L.jqplot.ElemContainer.call(this);this.axis;this.show=true;this.label="";this.fontFamily=null;this.fontSize=null;this.textColor=null;this._elem;this.escapeHTML=false;L.extend(true,this,ah)};L.jqplot.AxisLabelRenderer.prototype=new L.jqplot.ElemContainer();L.jqplot.AxisLabelRenderer.prototype.constructor=L.jqplot.AxisLabelRenderer;L.jqplot.AxisLabelRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.AxisLabelRenderer.prototype.draw=function(ah,ai){if(this._elem){this._elem.emptyForce();this._elem=null}this._elem=L('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>');if(Number(this.label)){this._elem.css("white-space","nowrap")}if(!this.escapeHTML){this._elem.html(this.label)}else{this._elem.text(this.label)}if(this.fontFamily){this._elem.css("font-family",this.fontFamily)}if(this.fontSize){this._elem.css("font-size",this.fontSize)}if(this.textColor){this._elem.css("color",this.textColor)}return this._elem};L.jqplot.AxisLabelRenderer.prototype.pack=function(){};L.jqplot.AxisTickRenderer=function(ah){L.jqplot.ElemContainer.call(this);this.mark="outside";this.axis;this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.size=4;this.markSize=6;this.show=true;this.showLabel=true;this.label=null;this.value=null;this._styles={};this.formatter=L.jqplot.DefaultTickFormatter;this.prefix="";this.suffix="";this.formatString="";this.fontFamily;this.fontSize;this.textColor;this.escapeHTML=false;this._elem;this._breakTick=false;L.extend(true,this,ah)};L.jqplot.AxisTickRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.AxisTickRenderer.prototype=new L.jqplot.ElemContainer();L.jqplot.AxisTickRenderer.prototype.constructor=L.jqplot.AxisTickRenderer;L.jqplot.AxisTickRenderer.prototype.setTick=function(ah,aj,ai){this.value=ah;this.axis=aj;if(ai){this.isMinorTick=true}return this};L.jqplot.AxisTickRenderer.prototype.draw=function(){if(this.label===null){this.label=this.prefix+this.formatter(this.formatString,this.value)+this.suffix}var ai={position:"absolute"};if(Number(this.label)){ai.whitSpace="nowrap"}if(this._elem){this._elem.emptyForce();this._elem=null}this._elem=L(document.createElement("div"));this._elem.addClass("jqplot-"+this.axis+"-tick");if(!this.escapeHTML){this._elem.html(this.label)}else{this._elem.text(this.label)}this._elem.css(ai);for(var ah in this._styles){this._elem.css(ah,this._styles[ah])}if(this.fontFamily){this._elem.css("font-family",this.fontFamily)}if(this.fontSize){this._elem.css("font-size",this.fontSize)}if(this.textColor){this._elem.css("color",this.textColor)}if(this._breakTick){this._elem.addClass("jqplot-breakTick")}return this._elem};L.jqplot.DefaultTickFormatter=function(ah,ai){if(typeof ai=="number"){if(!ah){ah=L.jqplot.config.defaultTickFormatString}return L.jqplot.sprintf(ah,ai)}else{return String(ai)}};L.jqplot.PercentTickFormatter=function(ah,ai){if(typeof ai=="number"){ai=100*ai;if(!ah){ah=L.jqplot.config.defaultTickFormatString}return L.jqplot.sprintf(ah,ai)}else{return String(ai)}};L.jqplot.AxisTickRenderer.prototype.pack=function(){};L.jqplot.CanvasGridRenderer=function(){this.shadowRenderer=new L.jqplot.ShadowRenderer()};L.jqplot.CanvasGridRenderer.prototype.init=function(ai){this._ctx;L.extend(true,this,ai);var ah={lineJoin:"miter",lineCap:"round",fill:false,isarc:false,angle:this.shadowAngle,offset:this.shadowOffset,alpha:this.shadowAlpha,depth:this.shadowDepth,lineWidth:this.shadowWidth,closePath:false,strokeStyle:this.shadowColor};this.renderer.shadowRenderer.init(ah)};L.jqplot.CanvasGridRenderer.prototype.createElement=function(ak){var aj;if(this._elem){if(L.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==u){aj=this._elem.get(0);window.G_vmlCanvasManager.uninitElement(aj);aj=null}this._elem.emptyForce();this._elem=null}aj=ak.canvasManager.getCanvas();var ah=this._plotDimensions.width;var ai=this._plotDimensions.height;aj.width=ah;aj.height=ai;this._elem=L(aj);this._elem.addClass("jqplot-grid-canvas");this._elem.css({position:"absolute",left:0,top:0});aj=ak.canvasManager.initCanvas(aj);this._top=this._offsets.top;this._bottom=ai-this._offsets.bottom;this._left=this._offsets.left;this._right=ah-this._offsets.right;this._width=this._right-this._left;this._height=this._bottom-this._top;aj=null;return this._elem};L.jqplot.CanvasGridRenderer.prototype.draw=function(){this._ctx=this._elem.get(0).getContext("2d");var at=this._ctx;var aw=this._axes;at.save();at.clearRect(0,0,this._plotDimensions.width,this._plotDimensions.height);at.fillStyle=this.backgroundColor||this.background;at.fillRect(this._left,this._top,this._width,this._height);at.save();at.lineJoin="miter";at.lineCap="butt";at.lineWidth=this.gridLineWidth;at.strokeStyle=this.gridLineColor;var aA,az,ap,aq;var am=["xaxis","yaxis","x2axis","y2axis"];for(var ay=4;ay>0;ay--){var aD=am[ay-1];var ah=aw[aD];var aB=ah._ticks;var ar=aB.length;if(ah.show){if(ah.drawBaseline){var aC={};if(ah.baselineWidth!==null){aC.lineWidth=ah.baselineWidth}if(ah.baselineColor!==null){aC.strokeStyle=ah.baselineColor}switch(aD){case"xaxis":ao(this._left,this._bottom,this._right,this._bottom,aC);break;case"yaxis":ao(this._left,this._bottom,this._left,this._top,aC);break;case"x2axis":ao(this._left,this._bottom,this._right,this._bottom,aC);break;case"y2axis":ao(this._right,this._bottom,this._right,this._top,aC);break}}for(var au=ar;au>0;au--){var an=aB[au-1];if(an.show){var ak=Math.round(ah.u2p(an.value))+0.5;switch(aD){case"xaxis":if(an.showGridline&&this.drawGridlines&&((!an.isMinorTick&&ah.drawMajorGridlines)||(an.isMinorTick&&ah.drawMinorGridlines))){ao(ak,this._top,ak,this._bottom)}if(an.showMark&&an.mark&&((!an.isMinorTick&&ah.drawMajorTickMarks)||(an.isMinorTick&&ah.drawMinorTickMarks))){ap=an.markSize;aq=an.mark;var ak=Math.round(ah.u2p(an.value))+0.5;switch(aq){case"outside":aA=this._bottom;az=this._bottom+ap;break;case"inside":aA=this._bottom-ap;az=this._bottom;break;case"cross":aA=this._bottom-ap;az=this._bottom+ap;break;default:aA=this._bottom;az=this._bottom+ap;break}if(this.shadow){this.renderer.shadowRenderer.draw(at,[[ak,aA],[ak,az]],{lineCap:"butt",lineWidth:this.gridLineWidth,offset:this.gridLineWidth*0.75,depth:2,fill:false,closePath:false})}ao(ak,aA,ak,az)}break;case"yaxis":if(an.showGridline&&this.drawGridlines&&((!an.isMinorTick&&ah.drawMajorGridlines)||(an.isMinorTick&&ah.drawMinorGridlines))){ao(this._right,ak,this._left,ak)}if(an.showMark&&an.mark&&((!an.isMinorTick&&ah.drawMajorTickMarks)||(an.isMinorTick&&ah.drawMinorTickMarks))){ap=an.markSize;aq=an.mark;var ak=Math.round(ah.u2p(an.value))+0.5;switch(aq){case"outside":aA=this._left-ap;az=this._left;break;case"inside":aA=this._left;az=this._left+ap;break;case"cross":aA=this._left-ap;az=this._left+ap;break;default:aA=this._left-ap;az=this._left;break}if(this.shadow){this.renderer.shadowRenderer.draw(at,[[aA,ak],[az,ak]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}ao(aA,ak,az,ak,{strokeStyle:ah.borderColor})}break;case"x2axis":if(an.showGridline&&this.drawGridlines&&((!an.isMinorTick&&ah.drawMajorGridlines)||(an.isMinorTick&&ah.drawMinorGridlines))){ao(ak,this._bottom,ak,this._top)}if(an.showMark&&an.mark&&((!an.isMinorTick&&ah.drawMajorTickMarks)||(an.isMinorTick&&ah.drawMinorTickMarks))){ap=an.markSize;aq=an.mark;var ak=Math.round(ah.u2p(an.value))+0.5;switch(aq){case"outside":aA=this._top-ap;az=this._top;break;case"inside":aA=this._top;az=this._top+ap;break;case"cross":aA=this._top-ap;az=this._top+ap;break;default:aA=this._top-ap;az=this._top;break}if(this.shadow){this.renderer.shadowRenderer.draw(at,[[ak,aA],[ak,az]],{lineCap:"butt",lineWidth:this.gridLineWidth,offset:this.gridLineWidth*0.75,depth:2,fill:false,closePath:false})}ao(ak,aA,ak,az)}break;case"y2axis":if(an.showGridline&&this.drawGridlines&&((!an.isMinorTick&&ah.drawMajorGridlines)||(an.isMinorTick&&ah.drawMinorGridlines))){ao(this._left,ak,this._right,ak)}if(an.showMark&&an.mark&&((!an.isMinorTick&&ah.drawMajorTickMarks)||(an.isMinorTick&&ah.drawMinorTickMarks))){ap=an.markSize;aq=an.mark;var ak=Math.round(ah.u2p(an.value))+0.5;switch(aq){case"outside":aA=this._right;az=this._right+ap;break;case"inside":aA=this._right-ap;az=this._right;break;case"cross":aA=this._right-ap;az=this._right+ap;break;default:aA=this._right;az=this._right+ap;break}if(this.shadow){this.renderer.shadowRenderer.draw(at,[[aA,ak],[az,ak]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}ao(aA,ak,az,ak,{strokeStyle:ah.borderColor})}break;default:break}}}an=null}ah=null;aB=null}am=["y3axis","y4axis","y5axis","y6axis","y7axis","y8axis","y9axis","yMidAxis"];for(var ay=7;ay>0;ay--){var ah=aw[am[ay-1]];var aB=ah._ticks;if(ah.show){var ai=aB[ah.numberTicks-1];var al=aB[0];var aj=ah.getLeft();var av=[[aj,ai.getTop()+ai.getHeight()/2],[aj,al.getTop()+al.getHeight()/2+1]];if(this.shadow){this.renderer.shadowRenderer.draw(at,av,{lineCap:"butt",fill:false,closePath:false})}ao(av[0][0],av[0][1],av[1][0],av[1][1],{lineCap:"butt",strokeStyle:ah.borderColor,lineWidth:ah.borderWidth});for(var au=aB.length;au>0;au--){var an=aB[au-1];ap=an.markSize;aq=an.mark;var ak=Math.round(ah.u2p(an.value))+0.5;if(an.showMark&&an.mark){switch(aq){case"outside":aA=aj;az=aj+ap;break;case"inside":aA=aj-ap;az=aj;break;case"cross":aA=aj-ap;az=aj+ap;break;default:aA=aj;az=aj+ap;break}av=[[aA,ak],[az,ak]];if(this.shadow){this.renderer.shadowRenderer.draw(at,av,{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}ao(aA,ak,az,ak,{strokeStyle:ah.borderColor})}an=null}al=null}ah=null;aB=null}at.restore();function ao(aH,aG,aE,ax,aF){at.save();aF=aF||{};if(aF.lineWidth==null||aF.lineWidth!=0){L.extend(true,at,aF);at.beginPath();at.moveTo(aH,aG);at.lineTo(aE,ax);at.stroke();at.restore()}}if(this.shadow){var av=[[this._left,this._bottom],[this._right,this._bottom],[this._right,this._top]];this.renderer.shadowRenderer.draw(at,av)}if(this.borderWidth!=0&&this.drawBorder){ao(this._left,this._top,this._right,this._top,{lineCap:"round",strokeStyle:aw.x2axis.borderColor,lineWidth:aw.x2axis.borderWidth});ao(this._right,this._top,this._right,this._bottom,{lineCap:"round",strokeStyle:aw.y2axis.borderColor,lineWidth:aw.y2axis.borderWidth});ao(this._right,this._bottom,this._left,this._bottom,{lineCap:"round",strokeStyle:aw.xaxis.borderColor,lineWidth:aw.xaxis.borderWidth});ao(this._left,this._bottom,this._left,this._top,{lineCap:"round",strokeStyle:aw.yaxis.borderColor,lineWidth:aw.yaxis.borderWidth})}at.restore();at=null;aw=null};L.jqplot.DivTitleRenderer=function(){};L.jqplot.DivTitleRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.DivTitleRenderer.prototype.draw=function(){if(this._elem){this._elem.emptyForce();this._elem=null}var ak=this.renderer;var aj=document.createElement("div");this._elem=L(aj);this._elem.addClass("jqplot-title");if(!this.text){this.show=false;this._elem.height(0);this._elem.width(0)}else{if(this.text){var ah;if(this.color){ah=this.color}else{if(this.textColor){ah=this.textColor}}var ai={position:"absolute",top:"0px",left:"0px"};if(this._plotWidth){ai.width=this._plotWidth+"px"}if(this.fontSize){ai.fontSize=this.fontSize}if(typeof this.textAlign==="string"){ai.textAlign=this.textAlign}else{ai.textAlign="center"}if(ah){ai.color=ah}if(this.paddingBottom){ai.paddingBottom=this.paddingBottom}if(this.fontFamily){ai.fontFamily=this.fontFamily}this._elem.css(ai);if(this.escapeHtml){this._elem.text(this.text)}else{this._elem.html(this.text)}}}aj=null;return this._elem};L.jqplot.DivTitleRenderer.prototype.pack=function(){};var r=0.1;L.jqplot.LinePattern=function(aw,aq){var ap={dotted:[r,L.jqplot.config.dotGapLength],dashed:[L.jqplot.config.dashLength,L.jqplot.config.gapLength],solid:null};if(typeof aq==="string"){if(aq[0]==="."||aq[0]==="-"){var ax=aq;aq=[];for(var ao=0,al=ax.length;ao<al;ao++){if(ax[ao]==="."){aq.push(r)}else{if(ax[ao]==="-"){aq.push(L.jqplot.config.dashLength)}else{continue}}aq.push(L.jqplot.config.gapLength)}}else{aq=ap[aq]}}if(!(aq&&aq.length)){return aw}var ak=0;var ar=aq[0];var au=0;var at=0;var an=0;var ah=0;var av=function(ay,az){aw.moveTo(ay,az);au=ay;at=az;an=ay;ah=az};var aj=function(ay,aE){var aC=aw.lineWidth;var aA=ay-au;var az=aE-at;var aB=Math.sqrt(aA*aA+az*az);if((aB>0)&&(aC>0)){aA/=aB;az/=aB;while(true){var aD=aC*ar;if(aD<aB){au+=aD*aA;at+=aD*az;if((ak&1)==0){aw.lineTo(au,at)}else{aw.moveTo(au,at)}aB-=aD;ak++;if(ak>=aq.length){ak=0}ar=aq[ak]}else{au=ay;at=aE;if((ak&1)==0){aw.lineTo(au,at)}else{aw.moveTo(au,at)}ar-=aB/aC;break}}}};var ai=function(){aw.beginPath()};var am=function(){aj(an,ah)};return{moveTo:av,lineTo:aj,beginPath:ai,closePath:am}};L.jqplot.LineRenderer=function(){this.shapeRenderer=new L.jqplot.ShapeRenderer();this.shadowRenderer=new L.jqplot.ShadowRenderer()};L.jqplot.LineRenderer.prototype.init=function(ai,an){ai=ai||{};this._type="line";this.renderer.animation={show:false,direction:"left",speed:2500,_supported:true};this.renderer.smooth=false;this.renderer.tension=null;this.renderer.constrainSmoothing=true;this.renderer._smoothedData=[];this.renderer._smoothedPlotData=[];this.renderer._hiBandGridData=[];this.renderer._lowBandGridData=[];this.renderer._hiBandSmoothedData=[];this.renderer._lowBandSmoothedData=[];this.renderer.bandData=[];this.renderer.bands={show:false,hiData:[],lowData:[],color:this.color,showLines:false,fill:true,fillColor:null,_min:null,_max:null,interval:"3%"};var al={highlightMouseOver:ai.highlightMouseOver,highlightMouseDown:ai.highlightMouseDown,highlightColor:ai.highlightColor};delete (ai.highlightMouseOver);delete (ai.highlightMouseDown);delete (ai.highlightColor);L.extend(true,this.renderer,ai);this.renderer.options=ai;if(this.renderer.bandData.length>1&&(!ai.bands||ai.bands.show==null)){this.renderer.bands.show=true}else{if(ai.bands&&ai.bands.show==null&&ai.bands.interval!=null){this.renderer.bands.show=true}}if(this.fill){this.renderer.bands.show=false}if(this.renderer.bands.show){this.renderer.initBands.call(this,this.renderer.options,an)}if(this._stack){this.renderer.smooth=false}var am={lineJoin:this.lineJoin,lineCap:this.lineCap,fill:this.fill,isarc:false,strokeStyle:this.color,fillStyle:this.fillColor,lineWidth:this.lineWidth,linePattern:this.linePattern,closePath:this.fill};this.renderer.shapeRenderer.init(am);var aj=ai.shadowOffset;if(aj==null){if(this.lineWidth>2.5){aj=1.25*(1+(Math.atan((this.lineWidth/2.5))/0.785398163-1)*0.6)}else{aj=1.25*Math.atan((this.lineWidth/2.5))/0.785398163}}var ah={lineJoin:this.lineJoin,lineCap:this.lineCap,fill:this.fill,isarc:false,angle:this.shadowAngle,offset:aj,alpha:this.shadowAlpha,depth:this.shadowDepth,lineWidth:this.lineWidth,linePattern:this.linePattern,closePath:this.fill};this.renderer.shadowRenderer.init(ah);this._areaPoints=[];this._boundingBox=[[],[]];if(!this.isTrendline&&this.fill||this.renderer.bands.show){this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColor=null;if(al.highlightMouseDown&&al.highlightMouseOver==null){al.highlightMouseOver=false}L.extend(true,this,{highlightMouseOver:al.highlightMouseOver,highlightMouseDown:al.highlightMouseDown,highlightColor:al.highlightColor});if(!this.highlightColor){var ak=(this.renderer.bands.show)?this.renderer.bands.fillColor:this.fillColor;this.highlightColor=L.jqplot.computeHighlightColors(ak)}if(this.highlighter){this.highlighter.show=false}}if(!this.isTrendline&&an){an.plugins.lineRenderer={};an.postInitHooks.addOnce(z);an.postDrawHooks.addOnce(af);an.eventListenerHooks.addOnce("jqplotMouseMove",h);an.eventListenerHooks.addOnce("jqplotMouseDown",e);an.eventListenerHooks.addOnce("jqplotMouseUp",ad);an.eventListenerHooks.addOnce("jqplotClick",g);an.eventListenerHooks.addOnce("jqplotRightClick",s)}};L.jqplot.LineRenderer.prototype.initBands=function(ak,av){var al=ak.bandData||[];var an=this.renderer.bands;an.hiData=[];an.lowData=[];var aB=this.data;an._max=null;an._min=null;if(al.length==2){if(L.isArray(al[0][0])){var ao;var ah=0,ar=0;for(var aw=0,at=al[0].length;aw<at;aw++){ao=al[0][aw];if((ao[1]!=null&&ao[1]>an._max)||an._max==null){an._max=ao[1]}if((ao[1]!=null&&ao[1]<an._min)||an._min==null){an._min=ao[1]}}for(var aw=0,at=al[1].length;aw<at;aw++){ao=al[1][aw];if((ao[1]!=null&&ao[1]>an._max)||an._max==null){an._max=ao[1];ar=1}if((ao[1]!=null&&ao[1]<an._min)||an._min==null){an._min=ao[1];ah=1}}if(ar===ah){an.show=false}an.hiData=al[ar];an.lowData=al[ah]}else{if(al[0].length===aB.length&&al[1].length===aB.length){var aj=(al[0][0]>al[1][0])?0:1;var aC=(aj)?0:1;for(var aw=0,at=aB.length;aw<at;aw++){an.hiData.push([aB[aw][0],al[aj][aw]]);an.lowData.push([aB[aw][0],al[aC][aw]])}}else{an.show=false}}}else{if(al.length>2&&!L.isArray(al[0][0])){var aj=(al[0][0]>al[0][1])?0:1;var aC=(aj)?0:1;for(var aw=0,at=al.length;aw<at;aw++){an.hiData.push([aB[aw][0],al[aw][aj]]);an.lowData.push([aB[aw][0],al[aw][aC]])}}else{var aq=an.interval;var aA=null;var az=null;var ai=null;var au=null;if(L.isArray(aq)){aA=aq[0];az=aq[1]}else{aA=aq}if(isNaN(aA)){if(aA.charAt(aA.length-1)==="%"){ai="multiply";aA=parseFloat(aA)/100+1}}else{aA=parseFloat(aA);ai="add"}if(az!==null&&isNaN(az)){if(az.charAt(az.length-1)==="%"){au="multiply";az=parseFloat(az)/100+1}}else{if(az!==null){az=parseFloat(az);au="add"}}if(aA!==null){if(az===null){az=-aA;au=ai;if(au==="multiply"){az+=2}}if(aA<az){var ax=aA;aA=az;az=ax;ax=ai;ai=au;au=ax}for(var aw=0,at=aB.length;aw<at;aw++){switch(ai){case"add":an.hiData.push([aB[aw][0],aB[aw][1]+aA]);break;case"multiply":an.hiData.push([aB[aw][0],aB[aw][1]*aA]);break}switch(au){case"add":an.lowData.push([aB[aw][0],aB[aw][1]+az]);break;case"multiply":an.lowData.push([aB[aw][0],aB[aw][1]*az]);break}}}else{an.show=false}}}var am=an.hiData;var ap=an.lowData;for(var aw=0,at=am.length;aw<at;aw++){if((am[aw][1]!=null&&am[aw][1]>an._max)||an._max==null){an._max=am[aw][1]}}for(var aw=0,at=ap.length;aw<at;aw++){if((ap[aw][1]!=null&&ap[aw][1]<an._min)||an._min==null){an._min=ap[aw][1]}}if(an.fillColor===null){var ay=L.jqplot.getColorComponents(an.color);ay[3]=ay[3]*0.5;an.fillColor="rgba("+ay[0]+", "+ay[1]+", "+ay[2]+", "+ay[3]+")"}};function K(ai,ah){return(3.4182054+ah)*Math.pow(ai,-0.3534992)}function n(aj,ai){var ah=Math.sqrt(Math.pow((ai[0]-aj[0]),2)+Math.pow((ai[1]-aj[1]),2));return 5.7648*Math.log(ah)+7.4456}function A(ah){var ai=(Math.exp(2*ah)-1)/(Math.exp(2*ah)+1);return ai}function J(aJ){var at=this.renderer.smooth;var aD=this.canvas.getWidth();var an=this._xaxis.series_p2u;var aG=this._yaxis.series_p2u;var aF=null;var am=null;var az=aJ.length/aD;var aj=[];var ay=[];if(!isNaN(parseFloat(at))){aF=parseFloat(at)}else{aF=K(az,0.5)}var aw=[];var ak=[];for(var aE=0,aA=aJ.length;aE<aA;aE++){aw.push(aJ[aE][1]);ak.push(aJ[aE][0])}function av(aK,aL){if(aK-aL==0){return Math.pow(10,10)}else{return aK-aL}}var ax,ar,aq,ap;var ah=aJ.length-1;for(var al=1,aB=aJ.length;al<aB;al++){var ai=[];var au=[];for(var aC=0;aC<2;aC++){var aE=al-1+aC;if(aE==0||aE==ah){ai[aC]=Math.pow(10,10)}else{if(aw[aE+1]-aw[aE]==0||aw[aE]-aw[aE-1]==0){ai[aC]=0}else{if(((ak[aE+1]-ak[aE])/(aw[aE+1]-aw[aE])+(ak[aE]-ak[aE-1])/(aw[aE]-aw[aE-1]))==0){ai[aC]=0}else{if((aw[aE+1]-aw[aE])*(aw[aE]-aw[aE-1])<0){ai[aC]=0}else{ai[aC]=2/(av(ak[aE+1],ak[aE])/(aw[aE+1]-aw[aE])+av(ak[aE],ak[aE-1])/(aw[aE]-aw[aE-1]))}}}}}if(al==1){ai[0]=3/2*(aw[1]-aw[0])/av(ak[1],ak[0])-ai[1]/2}else{if(al==ah){ai[1]=3/2*(aw[ah]-aw[ah-1])/av(ak[ah],ak[ah-1])-ai[0]/2}}au[0]=-2*(ai[1]+2*ai[0])/av(ak[al],ak[al-1])+6*(aw[al]-aw[al-1])/Math.pow(av(ak[al],ak[al-1]),2);au[1]=2*(2*ai[1]+ai[0])/av(ak[al],ak[al-1])-6*(aw[al]-aw[al-1])/Math.pow(av(ak[al],ak[al-1]),2);ap=1/6*(au[1]-au[0])/av(ak[al],ak[al-1]);aq=1/2*(ak[al]*au[0]-ak[al-1]*au[1])/av(ak[al],ak[al-1]);ar=(aw[al]-aw[al-1]-aq*(Math.pow(ak[al],2)-Math.pow(ak[al-1],2))-ap*(Math.pow(ak[al],3)-Math.pow(ak[al-1],3)))/av(ak[al],ak[al-1]);ax=aw[al-1]-ar*ak[al-1]-aq*Math.pow(ak[al-1],2)-ap*Math.pow(ak[al-1],3);var aI=(ak[al]-ak[al-1])/aF;var aH,ao;for(var aC=0,aA=aF;aC<aA;aC++){aH=[];ao=ak[al-1]+aC*aI;aH.push(ao);aH.push(ax+ar*ao+aq*Math.pow(ao,2)+ap*Math.pow(ao,3));aj.push(aH);ay.push([an(aH[0]),aG(aH[1])])}}aj.push(aJ[aE]);ay.push([an(aJ[aE][0]),aG(aJ[aE][1])]);return[aj,ay]}function F(ap){var ao=this.renderer.smooth;var aU=this.renderer.tension;var ah=this.canvas.getWidth();var aH=this._xaxis.series_p2u;var aq=this._yaxis.series_p2u;var aI=null;var aJ=null;var aT=null;var aO=null;var aM=null;var at=null;var aR=null;var am=null;var aK,aL,aD,aC,aA,ay;var ak,ai,av,au;var aB,az,aN;var aw=[];var aj=[];var al=ap.length/ah;var aS,ax,aF,aG,aE;var ar=[];var an=[];if(!isNaN(parseFloat(ao))){aI=parseFloat(ao)}else{aI=K(al,0.5)}if(!isNaN(parseFloat(aU))){aU=parseFloat(aU)}for(var aQ=0,aP=ap.length-1;aQ<aP;aQ++){if(aU===null){at=Math.abs((ap[aQ+1][1]-ap[aQ][1])/(ap[aQ+1][0]-ap[aQ][0]));aS=0.3;ax=0.6;aF=(ax-aS)/2;aG=2.5;aE=-1.4;am=at/aG+aE;aO=aF*A(am)-aF*A(aE)+aS;if(aQ>0){aR=Math.abs((ap[aQ][1]-ap[aQ-1][1])/(ap[aQ][0]-ap[aQ-1][0]))}am=aR/aG+aE;aM=aF*A(am)-aF*A(aE)+aS;aT=(aO+aM)/2}else{aT=aU}for(aK=0;aK<aI;aK++){aL=aK/aI;aD=(1+2*aL)*Math.pow((1-aL),2);aC=aL*Math.pow((1-aL),2);aA=Math.pow(aL,2)*(3-2*aL);ay=Math.pow(aL,2)*(aL-1);if(ap[aQ-1]){ak=aT*(ap[aQ+1][0]-ap[aQ-1][0]);ai=aT*(ap[aQ+1][1]-ap[aQ-1][1])}else{ak=aT*(ap[aQ+1][0]-ap[aQ][0]);ai=aT*(ap[aQ+1][1]-ap[aQ][1])}if(ap[aQ+2]){av=aT*(ap[aQ+2][0]-ap[aQ][0]);au=aT*(ap[aQ+2][1]-ap[aQ][1])}else{av=aT*(ap[aQ+1][0]-ap[aQ][0]);au=aT*(ap[aQ+1][1]-ap[aQ][1])}aB=aD*ap[aQ][0]+aA*ap[aQ+1][0]+aC*ak+ay*av;az=aD*ap[aQ][1]+aA*ap[aQ+1][1]+aC*ai+ay*au;aN=[aB,az];ar.push(aN);an.push([aH(aB),aq(az)])}}ar.push(ap[aP]);an.push([aH(ap[aP][0]),aq(ap[aP][1])]);return[ar,an]}L.jqplot.LineRenderer.prototype.setGridData=function(ap){var al=this._xaxis.series_u2p;var ah=this._yaxis.series_u2p;var am=this._plotData;var aq=this._prevPlotData;this.gridData=[];this._prevGridData=[];this.renderer._smoothedData=[];this.renderer._smoothedPlotData=[];this.renderer._hiBandGridData=[];this.renderer._lowBandGridData=[];this.renderer._hiBandSmoothedData=[];this.renderer._lowBandSmoothedData=[];var ak=this.renderer.bands;var ai=false;for(var an=0,aj=am.length;an<aj;an++){if(am[an][0]!=null&&am[an][1]!=null){this.gridData.push([al.call(this._xaxis,am[an][0]),ah.call(this._yaxis,am[an][1])])}else{if(am[an][0]==null){ai=true;this.gridData.push([null,ah.call(this._yaxis,am[an][1])])}else{if(am[an][1]==null){ai=true;this.gridData.push([al.call(this._xaxis,am[an][0]),null])}}}if(aq[an]!=null&&aq[an][0]!=null&&aq[an][1]!=null){this._prevGridData.push([al.call(this._xaxis,aq[an][0]),ah.call(this._yaxis,aq[an][1])])}else{if(aq[an]!=null&&aq[an][0]==null){this._prevGridData.push([null,ah.call(this._yaxis,aq[an][1])])}else{if(aq[an]!=null&&aq[an][0]!=null&&aq[an][1]==null){this._prevGridData.push([al.call(this._xaxis,aq[an][0]),null])}}}}if(ai){this.renderer.smooth=false;if(this._type==="line"){ak.show=false}}if(this._type==="line"&&ak.show){for(var an=0,aj=ak.hiData.length;an<aj;an++){this.renderer._hiBandGridData.push([al.call(this._xaxis,ak.hiData[an][0]),ah.call(this._yaxis,ak.hiData[an][1])])}for(var an=0,aj=ak.lowData.length;an<aj;an++){this.renderer._lowBandGridData.push([al.call(this._xaxis,ak.lowData[an][0]),ah.call(this._yaxis,ak.lowData[an][1])])}}if(this._type==="line"&&this.renderer.smooth&&this.gridData.length>2){var ao;if(this.renderer.constrainSmoothing){ao=J.call(this,this.gridData);this.renderer._smoothedData=ao[0];this.renderer._smoothedPlotData=ao[1];if(ak.show){ao=J.call(this,this.renderer._hiBandGridData);this.renderer._hiBandSmoothedData=ao[0];ao=J.call(this,this.renderer._lowBandGridData);this.renderer._lowBandSmoothedData=ao[0]}ao=null}else{ao=F.call(this,this.gridData);this.renderer._smoothedData=ao[0];this.renderer._smoothedPlotData=ao[1];if(ak.show){ao=F.call(this,this.renderer._hiBandGridData);this.renderer._hiBandSmoothedData=ao[0];ao=F.call(this,this.renderer._lowBandGridData);this.renderer._lowBandSmoothedData=ao[0]}ao=null}}};L.jqplot.LineRenderer.prototype.makeGridData=function(ao,aq){var am=this._xaxis.series_u2p;var ah=this._yaxis.series_u2p;var ar=[];var aj=[];this.renderer._smoothedData=[];this.renderer._smoothedPlotData=[];this.renderer._hiBandGridData=[];this.renderer._lowBandGridData=[];this.renderer._hiBandSmoothedData=[];this.renderer._lowBandSmoothedData=[];var al=this.renderer.bands;var ai=false;for(var an=0;an<ao.length;an++){if(ao[an][0]!=null&&ao[an][1]!=null){ar.push([am.call(this._xaxis,ao[an][0]),ah.call(this._yaxis,ao[an][1])])}else{if(ao[an][0]==null){ai=true;ar.push([null,ah.call(this._yaxis,ao[an][1])])}else{if(ao[an][1]==null){ai=true;ar.push([am.call(this._xaxis,ao[an][0]),null])}}}}if(ai){this.renderer.smooth=false;if(this._type==="line"){al.show=false}}if(this._type==="line"&&al.show){for(var an=0,ak=al.hiData.length;an<ak;an++){this.renderer._hiBandGridData.push([am.call(this._xaxis,al.hiData[an][0]),ah.call(this._yaxis,al.hiData[an][1])])}for(var an=0,ak=al.lowData.length;an<ak;an++){this.renderer._lowBandGridData.push([am.call(this._xaxis,al.lowData[an][0]),ah.call(this._yaxis,al.lowData[an][1])])}}if(this._type==="line"&&this.renderer.smooth&&ar.length>2){var ap;if(this.renderer.constrainSmoothing){ap=J.call(this,ar);this.renderer._smoothedData=ap[0];this.renderer._smoothedPlotData=ap[1];if(al.show){ap=J.call(this,this.renderer._hiBandGridData);this.renderer._hiBandSmoothedData=ap[0];ap=J.call(this,this.renderer._lowBandGridData);this.renderer._lowBandSmoothedData=ap[0]}ap=null}else{ap=F.call(this,ar);this.renderer._smoothedData=ap[0];this.renderer._smoothedPlotData=ap[1];if(al.show){ap=F.call(this,this.renderer._hiBandGridData);this.renderer._hiBandSmoothedData=ap[0];ap=F.call(this,this.renderer._lowBandGridData);this.renderer._lowBandSmoothedData=ap[0]}ap=null}}return ar};L.jqplot.LineRenderer.prototype.draw=function(ax,aI,ai,aB){var aC;var aq=L.extend(true,{},ai);var ak=(aq.shadow!=u)?aq.shadow:this.shadow;var aJ=(aq.showLine!=u)?aq.showLine:this.showLine;var aA=(aq.fill!=u)?aq.fill:this.fill;var ah=(aq.fillAndStroke!=u)?aq.fillAndStroke:this.fillAndStroke;var ar,ay,av,aE;ax.save();if(aI.length){if(aJ){if(aA){if(this.fillToZero){var aF=this.negativeColor;if(!this.useNegativeColors){aF=aq.fillStyle}var ao=false;var ap=aq.fillStyle;if(ah){var aH=aI.slice(0)}if(this.index==0||!this._stack){var aw=[];var aL=(this.renderer.smooth)?this.renderer._smoothedPlotData:this._plotData;this._areaPoints=[];var aG=this._yaxis.series_u2p(this.fillToValue);var aj=this._xaxis.series_u2p(this.fillToValue);aq.closePath=true;if(this.fillAxis=="y"){aw.push([aI[0][0],aG]);this._areaPoints.push([aI[0][0],aG]);for(var aC=0;aC<aI.length-1;aC++){aw.push(aI[aC]);this._areaPoints.push(aI[aC]);if(aL[aC][1]*aL[aC+1][1]<=0){if(aL[aC][1]<0){ao=true;aq.fillStyle=aF}else{ao=false;aq.fillStyle=ap}var an=aI[aC][0]+(aI[aC+1][0]-aI[aC][0])*(aG-aI[aC][1])/(aI[aC+1][1]-aI[aC][1]);aw.push([an,aG]);this._areaPoints.push([an,aG]);if(ak){this.renderer.shadowRenderer.draw(ax,aw,aq)}this.renderer.shapeRenderer.draw(ax,aw,aq);aw=[[an,aG]]}}if(aL[aI.length-1][1]<0){ao=true;aq.fillStyle=aF}else{ao=false;aq.fillStyle=ap}aw.push(aI[aI.length-1]);this._areaPoints.push(aI[aI.length-1]);aw.push([aI[aI.length-1][0],aG]);this._areaPoints.push([aI[aI.length-1][0],aG])}if(ak){this.renderer.shadowRenderer.draw(ax,aw,aq)}this.renderer.shapeRenderer.draw(ax,aw,aq)}else{var au=this._prevGridData;for(var aC=au.length;aC>0;aC--){aI.push(au[aC-1])}if(ak){this.renderer.shadowRenderer.draw(ax,aI,aq)}this._areaPoints=aI;this.renderer.shapeRenderer.draw(ax,aI,aq)}}else{if(ah){var aH=aI.slice(0)}if(this.index==0||!this._stack){var al=ax.canvas.height;aI.unshift([aI[0][0],al]);var aD=aI.length;aI.push([aI[aD-1][0],al])}else{var au=this._prevGridData;for(var aC=au.length;aC>0;aC--){aI.push(au[aC-1])}}this._areaPoints=aI;if(ak){this.renderer.shadowRenderer.draw(ax,aI,aq)}this.renderer.shapeRenderer.draw(ax,aI,aq)}if(ah){var az=L.extend(true,{},aq,{fill:false,closePath:false});this.renderer.shapeRenderer.draw(ax,aH,az);if(this.markerRenderer.show){if(this.renderer.smooth){aH=this.gridData}for(aC=0;aC<aH.length;aC++){this.markerRenderer.draw(aH[aC][0],aH[aC][1],ax,aq.markerOptions)}}}}else{if(this.renderer.bands.show){var am;var aK=L.extend(true,{},aq);if(this.renderer.bands.showLines){am=(this.renderer.smooth)?this.renderer._hiBandSmoothedData:this.renderer._hiBandGridData;this.renderer.shapeRenderer.draw(ax,am,aq);am=(this.renderer.smooth)?this.renderer._lowBandSmoothedData:this.renderer._lowBandGridData;this.renderer.shapeRenderer.draw(ax,am,aK)}if(this.renderer.bands.fill){if(this.renderer.smooth){am=this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse())}else{am=this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse())}this._areaPoints=am;aK.closePath=true;aK.fill=true;aK.fillStyle=this.renderer.bands.fillColor;this.renderer.shapeRenderer.draw(ax,am,aK)}}if(ak){this.renderer.shadowRenderer.draw(ax,aI,aq)}this.renderer.shapeRenderer.draw(ax,aI,aq)}}var ar=av=ay=aE=null;for(aC=0;aC<this._areaPoints.length;aC++){var at=this._areaPoints[aC];if(ar>at[0]||ar==null){ar=at[0]}if(aE<at[1]||aE==null){aE=at[1]}if(av<at[0]||av==null){av=at[0]}if(ay>at[1]||ay==null){ay=at[1]}}if(this.type==="line"&&this.renderer.bands.show){aE=this._yaxis.series_u2p(this.renderer.bands._min);ay=this._yaxis.series_u2p(this.renderer.bands._max)}this._boundingBox=[[ar,aE],[av,ay]];if(this.markerRenderer.show&&!aA){if(this.renderer.smooth){aI=this.gridData}for(aC=0;aC<aI.length;aC++){if(aI[aC][0]!=null&&aI[aC][1]!=null){this.markerRenderer.draw(aI[aC][0],aI[aC][1],ax,aq.markerOptions)}}}}ax.restore()};L.jqplot.LineRenderer.prototype.drawShadow=function(ah,aj,ai){};function z(ak,aj,ah){for(var ai=0;ai<this.series.length;ai++){if(this.series[ai].renderer.constructor==L.jqplot.LineRenderer){if(this.series[ai].highlightMouseOver){this.series[ai].highlightMouseDown=false}}}}function af(){if(this.plugins.lineRenderer&&this.plugins.lineRenderer.highlightCanvas){this.plugins.lineRenderer.highlightCanvas.resetCanvas();this.plugins.lineRenderer.highlightCanvas=null}this.plugins.lineRenderer.highlightedSeriesIndex=null;this.plugins.lineRenderer.highlightCanvas=new L.jqplot.GenericCanvas();this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-lineRenderer-highlight-canvas",this._plotDimensions,this));this.plugins.lineRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(ah){aa(ah.data.plot)})}function ac(an,am,ak,aj){var ai=an.series[am];var ah=an.plugins.lineRenderer.highlightCanvas;ah._ctx.clearRect(0,0,ah._ctx.canvas.width,ah._ctx.canvas.height);ai._highlightedPoint=ak;an.plugins.lineRenderer.highlightedSeriesIndex=am;var al={fillStyle:ai.highlightColor};if(ai.type==="line"&&ai.renderer.bands.show){al.fill=true;al.closePath=true}ai.renderer.shapeRenderer.draw(ah._ctx,aj,al);ah=null}function aa(aj){var ah=aj.plugins.lineRenderer.highlightCanvas;ah._ctx.clearRect(0,0,ah._ctx.canvas.width,ah._ctx.canvas.height);for(var ai=0;ai<aj.series.length;ai++){aj.series[ai]._highlightedPoint=null}aj.plugins.lineRenderer.highlightedSeriesIndex=null;aj.target.trigger("jqplotDataUnhighlight");ah=null}function h(al,ak,ao,an,am){if(an){var aj=[an.seriesIndex,an.pointIndex,an.data];var ai=jQuery.Event("jqplotDataMouseOver");ai.pageX=al.pageX;ai.pageY=al.pageY;am.target.trigger(ai,aj);if(am.series[aj[0]].highlightMouseOver&&!(aj[0]==am.plugins.lineRenderer.highlightedSeriesIndex)){var ah=jQuery.Event("jqplotDataHighlight");ah.which=al.which;ah.pageX=al.pageX;ah.pageY=al.pageY;am.target.trigger(ah,aj);ac(am,an.seriesIndex,an.pointIndex,an.points)}}else{if(an==null){aa(am)}}}function e(ak,aj,an,am,al){if(am){var ai=[am.seriesIndex,am.pointIndex,am.data];if(al.series[ai[0]].highlightMouseDown&&!(ai[0]==al.plugins.lineRenderer.highlightedSeriesIndex)){var ah=jQuery.Event("jqplotDataHighlight");ah.which=ak.which;ah.pageX=ak.pageX;ah.pageY=ak.pageY;al.target.trigger(ah,ai);ac(al,am.seriesIndex,am.pointIndex,am.points)}}else{if(am==null){aa(al)}}}function ad(aj,ai,am,al,ak){var ah=ak.plugins.lineRenderer.highlightedSeriesIndex;if(ah!=null&&ak.series[ah].highlightMouseDown){aa(ak)}}function g(ak,aj,an,am,al){if(am){var ai=[am.seriesIndex,am.pointIndex,am.data];var ah=jQuery.Event("jqplotDataClick");ah.which=ak.which;ah.pageX=ak.pageX;ah.pageY=ak.pageY;al.target.trigger(ah,ai)}}function s(al,ak,ao,an,am){if(an){var aj=[an.seriesIndex,an.pointIndex,an.data];var ah=am.plugins.lineRenderer.highlightedSeriesIndex;if(ah!=null&&am.series[ah].highlightMouseDown){aa(am)}var ai=jQuery.Event("jqplotDataRightClick");ai.which=al.which;ai.pageX=al.pageX;ai.pageY=al.pageY;am.target.trigger(ai,aj)}}L.jqplot.LinearAxisRenderer=function(){};L.jqplot.LinearAxisRenderer.prototype.init=function(ah){this.breakPoints=null;this.breakTickLabel="≈";this.drawBaseline=true;this.baselineWidth=null;this.baselineColor=null;this.forceTickAt0=false;this.forceTickAt100=false;this.tickInset=0;this.minorTicks=0;this.alignTicks=false;this._autoFormatString="";this._overrideFormatString=false;this._scalefact=1;L.extend(true,this,ah);if(this.breakPoints){if(!L.isArray(this.breakPoints)){this.breakPoints=null}else{if(this.breakPoints.length<2||this.breakPoints[1]<=this.breakPoints[0]){this.breakPoints=null}}}if(this.numberTicks!=null&&this.numberTicks<2){this.numberTicks=2}this.resetDataBounds()};L.jqplot.LinearAxisRenderer.prototype.draw=function(ah,ao){if(this.show){this.renderer.createTicks.call(this,ao);var an=0;var ai;if(this._elem){this._elem.emptyForce();this._elem=null}this._elem=L(document.createElement("div"));this._elem.addClass("jqplot-axis jqplot-"+this.name);this._elem.css("position","absolute");if(this.name=="xaxis"||this.name=="x2axis"){this._elem.width(this._plotDimensions.width)}else{this._elem.height(this._plotDimensions.height)}this.labelOptions.axis=this.name;this._label=new this.labelRenderer(this.labelOptions);if(this._label.show){var am=this._label.draw(ah,ao);am.appendTo(this._elem);am=null}var al=this._ticks;var ak;for(var aj=0;aj<al.length;aj++){ak=al[aj];if(ak.show&&ak.showLabel&&(!ak.isMinorTick||this.showMinorTicks)){this._elem.append(ak.draw(ah,ao))}}ak=null;al=null}return this._elem};L.jqplot.LinearAxisRenderer.prototype.reset=function(){this.min=this._options.min;this.max=this._options.max;this.tickInterval=this._options.tickInterval;this.numberTicks=this._options.numberTicks;this._autoFormatString="";if(this._overrideFormatString&&this.tickOptions&&this.tickOptions.formatString){this.tickOptions.formatString=""}};L.jqplot.LinearAxisRenderer.prototype.set=function(){var ao=0;var aj;var ai=0;var an=0;var ah=(this._label==null)?false:this._label.show;if(this.show){var am=this._ticks;var al;for(var ak=0;ak<am.length;ak++){al=am[ak];if(!al._breakTick&&al.show&&al.showLabel&&(!al.isMinorTick||this.showMinorTicks)){if(this.name=="xaxis"||this.name=="x2axis"){aj=al._elem.outerHeight(true)}else{aj=al._elem.outerWidth(true)}if(aj>ao){ao=aj}}}al=null;am=null;if(ah){ai=this._label._elem.outerWidth(true);an=this._label._elem.outerHeight(true)}if(this.name=="xaxis"){ao=ao+an;this._elem.css({height:ao+"px",left:"0px",bottom:"0px"})}else{if(this.name=="x2axis"){ao=ao+an;this._elem.css({height:ao+"px",left:"0px",top:"0px"})}else{if(this.name=="yaxis"){ao=ao+ai;this._elem.css({width:ao+"px",left:"0px",top:"0px"});if(ah&&this._label.constructor==L.jqplot.AxisLabelRenderer){this._label._elem.css("width",ai+"px")}}else{ao=ao+ai;this._elem.css({width:ao+"px",right:"0px",top:"0px"});if(ah&&this._label.constructor==L.jqplot.AxisLabelRenderer){this._label._elem.css("width",ai+"px")}}}}}};L.jqplot.LinearAxisRenderer.prototype.createTicks=function(aj){var aT=this._ticks;var aK=this.ticks;var az=this.name;var aB=this._dataBounds;var ah=(this.name.charAt(0)==="x")?this._plotDimensions.width:this._plotDimensions.height;var an;var a6,aI;var ap,ao;var a4,a0;var aH=this.min;var a5=this.max;var aW=this.numberTicks;var ba=this.tickInterval;var am=30;this._scalefact=(Math.max(ah,am+1)-am)/300;if(aK.length){for(a0=0;a0<aK.length;a0++){var aO=aK[a0];var aU=new this.tickRenderer(this.tickOptions);if(L.isArray(aO)){aU.value=aO[0];if(this.breakPoints){if(aO[0]==this.breakPoints[0]){aU.label=this.breakTickLabel;aU._breakTick=true;aU.showGridline=false;aU.showMark=false}else{if(aO[0]>this.breakPoints[0]&&aO[0]<=this.breakPoints[1]){aU.show=false;aU.showGridline=false;aU.label=aO[1]}else{aU.label=aO[1]}}}else{aU.label=aO[1]}aU.setTick(aO[0],this.name);this._ticks.push(aU)}else{if(L.isPlainObject(aO)){L.extend(true,aU,aO);aU.axis=this.name;this._ticks.push(aU)}else{aU.value=aO;if(this.breakPoints){if(aO==this.breakPoints[0]){aU.label=this.breakTickLabel;aU._breakTick=true;aU.showGridline=false;aU.showMark=false}else{if(aO>this.breakPoints[0]&&aO<=this.breakPoints[1]){aU.show=false;aU.showGridline=false}}}aU.setTick(aO,this.name);this._ticks.push(aU)}}}this.numberTicks=aK.length;this.min=this._ticks[0].value;this.max=this._ticks[this.numberTicks-1].value;this.tickInterval=(this.max-this.min)/(this.numberTicks-1)}else{if(az=="xaxis"||az=="x2axis"){ah=this._plotDimensions.width}else{ah=this._plotDimensions.height}var ax=this.numberTicks;if(this.alignTicks){if(this.name==="x2axis"&&aj.axes.xaxis.show){ax=aj.axes.xaxis.numberTicks}else{if(this.name.charAt(0)==="y"&&this.name!=="yaxis"&&this.name!=="yMidAxis"&&aj.axes.yaxis.show){ax=aj.axes.yaxis.numberTicks}}}a6=((this.min!=null)?this.min:aB.min);aI=((this.max!=null)?this.max:aB.max);var av=aI-a6;var aS,ay;var at;if(this.tickOptions==null||!this.tickOptions.formatString){this._overrideFormatString=true}if(this.min==null||this.max==null&&this.tickInterval==null&&!this.autoscale){if(this.forceTickAt0){if(a6>0){a6=0}if(aI<0){aI=0}}if(this.forceTickAt100){if(a6>100){a6=100}if(aI<100){aI=100}}var aE=false,a1=false;if(this.min!=null){aE=true}else{if(this.max!=null){a1=true}}var aP=L.jqplot.LinearTickGenerator(a6,aI,this._scalefact,ax,aE,a1);var aw=(this.min!=null)?a6:a6+av*(this.padMin-1);var aQ=(this.max!=null)?aI:aI-av*(this.padMax-1);if(a6<aw||aI>aQ){aw=(this.min!=null)?a6:a6-av*(this.padMin-1);aQ=(this.max!=null)?aI:aI+av*(this.padMax-1);aP=L.jqplot.LinearTickGenerator(aw,aQ,this._scalefact,ax,aE,a1)}this.min=aP[0];this.max=aP[1];this.numberTicks=aP[2];this._autoFormatString=aP[3];this.tickInterval=aP[4]}else{if(a6==aI){var ai=0.05;if(a6>0){ai=Math.max(Math.log(a6)/Math.LN10,0.05)}a6-=ai;aI+=ai}if(this.autoscale&&this.min==null&&this.max==null){var ak,al,ar;var aC=false;var aN=false;var aA={min:null,max:null,average:null,stddev:null};for(var a0=0;a0<this._series.length;a0++){var aV=this._series[a0];var aD=(aV.fillAxis=="x")?aV._xaxis.name:aV._yaxis.name;if(this.name==aD){var aR=aV._plotValues[aV.fillAxis];var aG=aR[0];var a2=aR[0];for(var aZ=1;aZ<aR.length;aZ++){if(aR[aZ]<aG){aG=aR[aZ]}else{if(aR[aZ]>a2){a2=aR[aZ]}}}var au=(a2-aG)/a2;if(aV.renderer.constructor==L.jqplot.BarRenderer){if(aG>=0&&(aV.fillToZero||au>0.1)){aC=true}else{aC=false;if(aV.fill&&aV.fillToZero&&aG<0&&a2>0){aN=true}else{aN=false}}}else{if(aV.fill){if(aG>=0&&(aV.fillToZero||au>0.1)){aC=true}else{if(aG<0&&a2>0&&aV.fillToZero){aC=false;aN=true}else{aC=false;aN=false}}}else{if(aG<0){aC=false}}}}}if(aC){this.numberTicks=2+Math.ceil((ah-(this.tickSpacing-1))/this.tickSpacing);this.min=0;aH=0;al=aI/(this.numberTicks-1);at=Math.pow(10,Math.abs(Math.floor(Math.log(al)/Math.LN10)));if(al/at==parseInt(al/at,10)){al+=at}this.tickInterval=Math.ceil(al/at)*at;this.max=this.tickInterval*(this.numberTicks-1)}else{if(aN){this.numberTicks=2+Math.ceil((ah-(this.tickSpacing-1))/this.tickSpacing);var aJ=Math.ceil(Math.abs(a6)/av*(this.numberTicks-1));var a9=this.numberTicks-1-aJ;al=Math.max(Math.abs(a6/aJ),Math.abs(aI/a9));at=Math.pow(10,Math.abs(Math.floor(Math.log(al)/Math.LN10)));this.tickInterval=Math.ceil(al/at)*at;this.max=this.tickInterval*a9;this.min=-this.tickInterval*aJ}else{if(this.numberTicks==null){if(this.tickInterval){this.numberTicks=3+Math.ceil(av/this.tickInterval)}else{this.numberTicks=2+Math.ceil((ah-(this.tickSpacing-1))/this.tickSpacing)}}if(this.tickInterval==null){al=av/(this.numberTicks-1);if(al<1){at=Math.pow(10,Math.abs(Math.floor(Math.log(al)/Math.LN10)))}else{at=1}this.tickInterval=Math.ceil(al*at*this.pad)/at}else{at=1/this.tickInterval}ak=this.tickInterval*(this.numberTicks-1);ar=(ak-av)/2;if(this.min==null){this.min=Math.floor(at*(a6-ar))/at}if(this.max==null){this.max=this.min+ak}}}var aF=L.jqplot.getSignificantFigures(this.tickInterval);var aM;if(aF.digitsLeft>=aF.significantDigits){aM="%d"}else{var at=Math.max(0,5-aF.digitsLeft);at=Math.min(at,aF.digitsRight);aM="%."+at+"f"}this._autoFormatString=aM}else{aS=(this.min!=null)?this.min:a6-av*(this.padMin-1);ay=(this.max!=null)?this.max:aI+av*(this.padMax-1);av=ay-aS;if(this.numberTicks==null){if(this.tickInterval!=null){this.numberTicks=Math.ceil((ay-aS)/this.tickInterval)+1}else{if(ah>100){this.numberTicks=parseInt(3+(ah-100)/75,10)}else{this.numberTicks=2}}}if(this.tickInterval==null){this.tickInterval=av/(this.numberTicks-1)}if(this.max==null){ay=aS+this.tickInterval*(this.numberTicks-1)}if(this.min==null){aS=ay-this.tickInterval*(this.numberTicks-1)}var aF=L.jqplot.getSignificantFigures(this.tickInterval);var aM;if(aF.digitsLeft>=aF.significantDigits){aM="%d"}else{var at=Math.max(0,5-aF.digitsLeft);at=Math.min(at,aF.digitsRight);aM="%."+at+"f"}this._autoFormatString=aM;this.min=aS;this.max=ay}if(this.renderer.constructor==L.jqplot.LinearAxisRenderer&&this._autoFormatString==""){av=this.max-this.min;var a7=new this.tickRenderer(this.tickOptions);var aL=a7.formatString||L.jqplot.config.defaultTickFormatString;var aL=aL.match(L.jqplot.sprintf.regex)[0];var a3=0;if(aL){if(aL.search(/[fFeEgGpP]/)>-1){var aY=aL.match(/\%\.(\d{0,})?[eEfFgGpP]/);if(aY){a3=parseInt(aY[1],10)}else{a3=6}}else{if(aL.search(/[di]/)>-1){a3=0}}var aq=Math.pow(10,-a3);if(this.tickInterval<aq){if(aW==null&&ba==null){this.tickInterval=aq;if(a5==null&&aH==null){this.min=Math.floor(this._dataBounds.min/aq)*aq;if(this.min==this._dataBounds.min){this.min=this._dataBounds.min-this.tickInterval}this.max=Math.ceil(this._dataBounds.max/aq)*aq;if(this.max==this._dataBounds.max){this.max=this._dataBounds.max+this.tickInterval}var aX=(this.max-this.min)/this.tickInterval;aX=aX.toFixed(11);aX=Math.ceil(aX);this.numberTicks=aX+1}else{if(a5==null){var aX=(this._dataBounds.max-this.min)/this.tickInterval;aX=aX.toFixed(11);this.numberTicks=Math.ceil(aX)+2;this.max=this.min+this.tickInterval*(this.numberTicks-1)}else{if(aH==null){var aX=(this.max-this._dataBounds.min)/this.tickInterval;aX=aX.toFixed(11);this.numberTicks=Math.ceil(aX)+2;this.min=this.max-this.tickInterval*(this.numberTicks-1)}else{this.numberTicks=Math.ceil((a5-aH)/this.tickInterval)+1;this.min=Math.floor(aH*Math.pow(10,a3))/Math.pow(10,a3);this.max=Math.ceil(a5*Math.pow(10,a3))/Math.pow(10,a3);this.numberTicks=Math.ceil((this.max-this.min)/this.tickInterval)+1}}}}}}}}if(this._overrideFormatString&&this._autoFormatString!=""){this.tickOptions=this.tickOptions||{};this.tickOptions.formatString=this._autoFormatString}var aU,a8;for(var a0=0;a0<this.numberTicks;a0++){a4=this.min+a0*this.tickInterval;aU=new this.tickRenderer(this.tickOptions);aU.setTick(a4,this.name);this._ticks.push(aU);if(a0<this.numberTicks-1){for(var aZ=0;aZ<this.minorTicks;aZ++){a4+=this.tickInterval/(this.minorTicks+1);a8=L.extend(true,{},this.tickOptions,{name:this.name,value:a4,label:"",isMinorTick:true});aU=new this.tickRenderer(a8);this._ticks.push(aU)}}aU=null}}if(this.tickInset){this.min=this.min-this.tickInset*this.tickInterval;this.max=this.max+this.tickInset*this.tickInterval}aT=null};L.jqplot.LinearAxisRenderer.prototype.resetTickValues=function(aj){if(L.isArray(aj)&&aj.length==this._ticks.length){var ai;for(var ah=0;ah<aj.length;ah++){ai=this._ticks[ah];ai.value=aj[ah];ai.label=ai.formatter(ai.formatString,aj[ah]);ai.label=ai.prefix+ai.label;ai._elem.html(ai.label)}ai=null;this.min=L.jqplot.arrayMin(aj);this.max=L.jqplot.arrayMax(aj);this.pack()}};L.jqplot.LinearAxisRenderer.prototype.pack=function(aj,ai){aj=aj||{};ai=ai||this._offsets;var ay=this._ticks;var au=this.max;var at=this.min;var ao=ai.max;var am=ai.min;var aq=(this._label==null)?false:this._label.show;for(var ar in aj){this._elem.css(ar,aj[ar])}this._offsets=ai;var ak=ao-am;var al=au-at;if(this.breakPoints){al=al-this.breakPoints[1]+this.breakPoints[0];this.p2u=function(aA){return(aA-am)*al/ak+at};this.u2p=function(aA){if(aA>this.breakPoints[0]&&aA<this.breakPoints[1]){aA=this.breakPoints[0]}if(aA<=this.breakPoints[0]){return(aA-at)*ak/al+am}else{return(aA-this.breakPoints[1]+this.breakPoints[0]-at)*ak/al+am}};if(this.name.charAt(0)=="x"){this.series_u2p=function(aA){if(aA>this.breakPoints[0]&&aA<this.breakPoints[1]){aA=this.breakPoints[0]}if(aA<=this.breakPoints[0]){return(aA-at)*ak/al}else{return(aA-this.breakPoints[1]+this.breakPoints[0]-at)*ak/al}};this.series_p2u=function(aA){return aA*al/ak+at}}else{this.series_u2p=function(aA){if(aA>this.breakPoints[0]&&aA<this.breakPoints[1]){aA=this.breakPoints[0]}if(aA>=this.breakPoints[1]){return(aA-au)*ak/al}else{return(aA+this.breakPoints[1]-this.breakPoints[0]-au)*ak/al}};this.series_p2u=function(aA){return aA*al/ak+au}}}else{this.p2u=function(aA){return(aA-am)*al/ak+at};this.u2p=function(aA){return(aA-at)*ak/al+am};if(this.name=="xaxis"||this.name=="x2axis"){this.series_u2p=function(aA){return(aA-at)*ak/al};this.series_p2u=function(aA){return aA*al/ak+at}}else{this.series_u2p=function(aA){return(aA-au)*ak/al};this.series_p2u=function(aA){return aA*al/ak+au}}}if(this.show){if(this.name=="xaxis"||this.name=="x2axis"){for(var av=0;av<ay.length;av++){var ap=ay[av];if(ap.show&&ap.showLabel){var ah;if(ap.constructor==L.jqplot.CanvasAxisTickRenderer&&ap.angle){var ax=(this.name=="xaxis")?1:-1;switch(ap.labelPosition){case"auto":if(ax*ap.angle<0){ah=-ap.getWidth()+ap._textRenderer.height*Math.sin(-ap._textRenderer.angle)/2}else{ah=-ap._textRenderer.height*Math.sin(ap._textRenderer.angle)/2}break;case"end":ah=-ap.getWidth()+ap._textRenderer.height*Math.sin(-ap._textRenderer.angle)/2;break;case"start":ah=-ap._textRenderer.height*Math.sin(ap._textRenderer.angle)/2;break;case"middle":ah=-ap.getWidth()/2+ap._textRenderer.height*Math.sin(-ap._textRenderer.angle)/2;break;default:ah=-ap.getWidth()/2+ap._textRenderer.height*Math.sin(-ap._textRenderer.angle)/2;break}}else{ah=-ap.getWidth()/2}var az=this.u2p(ap.value)+ah+"px";ap._elem.css("left",az);ap.pack()}}if(aq){var an=this._label._elem.outerWidth(true);this._label._elem.css("left",am+ak/2-an/2+"px");if(this.name=="xaxis"){this._label._elem.css("bottom","0px")}else{this._label._elem.css("top","0px")}this._label.pack()}}else{for(var av=0;av<ay.length;av++){var ap=ay[av];if(ap.show&&ap.showLabel){var ah;if(ap.constructor==L.jqplot.CanvasAxisTickRenderer&&ap.angle){var ax=(this.name=="yaxis")?1:-1;switch(ap.labelPosition){case"auto":case"end":if(ax*ap.angle<0){ah=-ap._textRenderer.height*Math.cos(-ap._textRenderer.angle)/2}else{ah=-ap.getHeight()+ap._textRenderer.height*Math.cos(ap._textRenderer.angle)/2}break;case"start":if(ap.angle>0){ah=-ap._textRenderer.height*Math.cos(-ap._textRenderer.angle)/2}else{ah=-ap.getHeight()+ap._textRenderer.height*Math.cos(ap._textRenderer.angle)/2}break;case"middle":ah=-ap.getHeight()/2;break;default:ah=-ap.getHeight()/2;break}}else{ah=-ap.getHeight()/2}var az=this.u2p(ap.value)+ah+"px";ap._elem.css("top",az);ap.pack()}}if(aq){var aw=this._label._elem.outerHeight(true);this._label._elem.css("top",ao-ak/2-aw/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{this._label._elem.css("right","0px")}this._label.pack()}}}ay=null};function i(ai){var ah;ai=Math.abs(ai);if(ai>=10){ah="%d"}else{if(ai>1){if(ai===parseInt(ai,10)){ah="%d"}else{ah="%.1f"}}else{var aj=-Math.floor(Math.log(ai)/Math.LN10);ah="%."+aj+"f"}}return ah}var b=[0.1,0.2,0.3,0.4,0.5,0.8,1,2,3,4,5];var c=function(ai){var ah=b.indexOf(ai);if(ah>0){return b[ah-1]}else{return b[b.length-1]/100}};var k=function(ai){var ah=b.indexOf(ai);if(ah<b.length-1){return b[ah+1]}else{return b[0]*100}};function d(al,au,at){var aq=Math.floor(at/2);var ai=Math.ceil(at*1.5);var ak=Number.MAX_VALUE;var ah=(au-al);var ax;var ap;var ar;var ay=L.jqplot.getSignificantFigures;var aw;var an;var ao;var av;for(var am=0,aj=ai-aq+1;am<aj;am++){ao=aq+am;ax=ah/(ao-1);ap=ay(ax);ax=Math.abs(at-ao)+ap.digitsRight;if(ax<ak){ak=ax;ar=ao;av=ap.digitsRight}else{if(ax===ak){if(ap.digitsRight<av){ar=ao;av=ap.digitsRight}}}}aw=Math.max(av,Math.max(ay(al).digitsRight,ay(au).digitsRight));if(aw===0){an="%d"}else{an="%."+aw+"f"}ax=ah/(ar-1);return[al,au,ar,an,ax]}function W(ai,al){al=al||7;var ak=ai/(al-1);var aj=Math.pow(10,Math.floor(Math.log(ak)/Math.LN10));var am=ak/aj;var ah;if(aj<1){if(am>5){ah=10*aj}else{if(am>2){ah=5*aj}else{if(am>1){ah=2*aj}else{ah=aj}}}}else{if(am>5){ah=10*aj}else{if(am>4){ah=5*aj}else{if(am>3){ah=4*aj}else{if(am>2){ah=3*aj}else{if(am>1){ah=2*aj}else{ah=aj}}}}}}return ah}function Q(ai,ah){ah=ah||1;var ak=Math.floor(Math.log(ai)/Math.LN10);var am=Math.pow(10,ak);var al=ai/am;var aj;al=al/ah;if(al<=0.38){aj=0.1}else{if(al<=1.6){aj=0.2}else{if(al<=4){aj=0.5}else{if(al<=8){aj=1}else{if(al<=16){aj=2}else{aj=5}}}}}return aj*am}function x(aj,ai){var al=Math.floor(Math.log(aj)/Math.LN10);var an=Math.pow(10,al);var am=aj/an;var ah;var ak;am=am/ai;if(am<=0.38){ak=0.1}else{if(am<=1.6){ak=0.2}else{if(am<=4){ak=0.5}else{if(am<=8){ak=1}else{if(am<=16){ak=2}else{ak=5}}}}}ah=ak*an;return[ah,ak,an]}L.jqplot.LinearTickGenerator=function(an,aq,aj,ak,ao,ar){ao=(ao===null)?false:ao;ar=(ar===null||ao)?false:ar;if(an===aq){aq=(aq)?0:1}aj=aj||1;if(aq<an){var at=aq;aq=an;an=at}var ai=[];var aw=Q(aq-an,aj);var av=L.jqplot.getSignificantFigures;if(ak==null){if(!ao&&!ar){ai[0]=Math.floor(an/aw)*aw;ai[1]=Math.ceil(aq/aw)*aw;ai[2]=Math.round((ai[1]-ai[0])/aw+1);ai[3]=i(aw);ai[4]=aw}else{if(ao){ai[0]=an;ai[2]=Math.ceil((aq-an)/aw+1);ai[1]=an+(ai[2]-1)*aw;var au=av(an).digitsRight;var ap=av(aw).digitsRight;if(au<ap){ai[3]=i(aw)}else{ai[3]="%."+au+"f"}ai[4]=aw}else{if(ar){ai[1]=aq;ai[2]=Math.ceil((aq-an)/aw+1);ai[0]=aq-(ai[2]-1)*aw;var al=av(aq).digitsRight;var ap=av(aw).digitsRight;if(al<ap){ai[3]=i(aw)}else{ai[3]="%."+al+"f"}ai[4]=aw}}}}else{var am=[];am[0]=Math.floor(an/aw)*aw;am[1]=Math.ceil(aq/aw)*aw;am[2]=Math.round((am[1]-am[0])/aw+1);am[3]=i(aw);am[4]=aw;if(am[2]===ak){ai=am}else{var ah=W(am[1]-am[0],ak);ai[0]=am[0];ai[2]=ak;ai[4]=ah;ai[3]=i(ah);ai[1]=ai[0]+(ai[2]-1)*ai[4]}}return ai};L.jqplot.LinearTickGenerator.bestLinearInterval=Q;L.jqplot.LinearTickGenerator.bestInterval=W;L.jqplot.LinearTickGenerator.bestLinearComponents=x;L.jqplot.LinearTickGenerator.bestConstrainedInterval=d;L.jqplot.MarkerRenderer=function(ah){this.show=true;this.style="filledCircle";this.lineWidth=2;this.size=9;this.color="#666666";this.shadow=true;this.shadowAngle=45;this.shadowOffset=1;this.shadowDepth=3;this.shadowAlpha="0.07";this.shadowRenderer=new L.jqplot.ShadowRenderer();this.shapeRenderer=new L.jqplot.ShapeRenderer();L.extend(true,this,ah)};L.jqplot.MarkerRenderer.prototype.init=function(ah){L.extend(true,this,ah);var aj={angle:this.shadowAngle,offset:this.shadowOffset,alpha:this.shadowAlpha,lineWidth:this.lineWidth,depth:this.shadowDepth,closePath:true};if(this.style.indexOf("filled")!=-1){aj.fill=true}if(this.style.indexOf("ircle")!=-1){aj.isarc=true;aj.closePath=false}this.shadowRenderer.init(aj);var ai={fill:false,isarc:false,strokeStyle:this.color,fillStyle:this.color,lineWidth:this.lineWidth,closePath:true};if(this.style.indexOf("filled")!=-1){ai.fill=true}if(this.style.indexOf("ircle")!=-1){ai.isarc=true;ai.closePath=false}this.shapeRenderer.init(ai)};L.jqplot.MarkerRenderer.prototype.drawDiamond=function(aj,ai,am,al,ao){var ah=1.2;var ap=this.size/2/ah;var an=this.size/2*ah;var ak=[[aj-ap,ai],[aj,ai+an],[aj+ap,ai],[aj,ai-an]];if(this.shadow){this.shadowRenderer.draw(am,ak)}this.shapeRenderer.draw(am,ak,ao)};L.jqplot.MarkerRenderer.prototype.drawPlus=function(ak,aj,an,am,aq){var ai=1;var ar=this.size/2*ai;var ao=this.size/2*ai;var ap=[[ak,aj-ao],[ak,aj+ao]];var al=[[ak+ar,aj],[ak-ar,aj]];var ah=L.extend(true,{},this.options,{closePath:false});if(this.shadow){this.shadowRenderer.draw(an,ap,{closePath:false});this.shadowRenderer.draw(an,al,{closePath:false})}this.shapeRenderer.draw(an,ap,ah);this.shapeRenderer.draw(an,al,ah)};L.jqplot.MarkerRenderer.prototype.drawX=function(ak,aj,an,am,aq){var ai=1;var ar=this.size/2*ai;var ao=this.size/2*ai;var ah=L.extend(true,{},this.options,{closePath:false});var ap=[[ak-ar,aj-ao],[ak+ar,aj+ao]];var al=[[ak-ar,aj+ao],[ak+ar,aj-ao]];if(this.shadow){this.shadowRenderer.draw(an,ap,{closePath:false});this.shadowRenderer.draw(an,al,{closePath:false})}this.shapeRenderer.draw(an,ap,ah);this.shapeRenderer.draw(an,al,ah)};L.jqplot.MarkerRenderer.prototype.drawDash=function(aj,ai,am,al,ao){var ah=1;var ap=this.size/2*ah;var an=this.size/2*ah;var ak=[[aj-ap,ai],[aj+ap,ai]];if(this.shadow){this.shadowRenderer.draw(am,ak)}this.shapeRenderer.draw(am,ak,ao)};L.jqplot.MarkerRenderer.prototype.drawLine=function(am,al,ah,ak,ai){var aj=[am,al];if(this.shadow){this.shadowRenderer.draw(ah,aj)}this.shapeRenderer.draw(ah,aj,ai)};L.jqplot.MarkerRenderer.prototype.drawSquare=function(aj,ai,am,al,ao){var ah=1;var ap=this.size/2/ah;var an=this.size/2*ah;var ak=[[aj-ap,ai-an],[aj-ap,ai+an],[aj+ap,ai+an],[aj+ap,ai-an]];if(this.shadow){this.shadowRenderer.draw(am,ak)}this.shapeRenderer.draw(am,ak,ao)};L.jqplot.MarkerRenderer.prototype.drawCircle=function(ai,ao,ak,an,al){var ah=this.size/2;var aj=2*Math.PI;var am=[ai,ao,ah,0,aj,true];if(this.shadow){this.shadowRenderer.draw(ak,am)}this.shapeRenderer.draw(ak,am,al)};L.jqplot.MarkerRenderer.prototype.draw=function(ah,ak,ai,aj){aj=aj||{};if(aj.show==null||aj.show!=false){if(aj.color&&!aj.fillStyle){aj.fillStyle=aj.color}if(aj.color&&!aj.strokeStyle){aj.strokeStyle=aj.color}switch(this.style){case"diamond":this.drawDiamond(ah,ak,ai,false,aj);break;case"filledDiamond":this.drawDiamond(ah,ak,ai,true,aj);break;case"circle":this.drawCircle(ah,ak,ai,false,aj);break;case"filledCircle":this.drawCircle(ah,ak,ai,true,aj);break;case"square":this.drawSquare(ah,ak,ai,false,aj);break;case"filledSquare":this.drawSquare(ah,ak,ai,true,aj);break;case"x":this.drawX(ah,ak,ai,true,aj);break;case"plus":this.drawPlus(ah,ak,ai,true,aj);break;case"dash":this.drawDash(ah,ak,ai,true,aj);break;case"line":this.drawLine(ah,ak,ai,false,aj);break;default:this.drawDiamond(ah,ak,ai,false,aj);break}}};L.jqplot.ShadowRenderer=function(ah){this.angle=45;this.offset=1;this.alpha=0.07;this.lineWidth=1.5;this.lineJoin="miter";this.lineCap="round";this.closePath=false;this.fill=false;this.depth=3;this.strokeStyle="rgba(0,0,0,0.1)";this.isarc=false;L.extend(true,this,ah)};L.jqplot.ShadowRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.ShadowRenderer.prototype.draw=function(av,at,ax){av.save();var ah=(ax!=null)?ax:{};var au=(ah.fill!=null)?ah.fill:this.fill;var ap=(ah.fillRect!=null)?ah.fillRect:this.fillRect;var ao=(ah.closePath!=null)?ah.closePath:this.closePath;var al=(ah.offset!=null)?ah.offset:this.offset;var aj=(ah.alpha!=null)?ah.alpha:this.alpha;var an=(ah.depth!=null)?ah.depth:this.depth;var aw=(ah.isarc!=null)?ah.isarc:this.isarc;var aq=(ah.linePattern!=null)?ah.linePattern:this.linePattern;av.lineWidth=(ah.lineWidth!=null)?ah.lineWidth:this.lineWidth;av.lineJoin=(ah.lineJoin!=null)?ah.lineJoin:this.lineJoin;av.lineCap=(ah.lineCap!=null)?ah.lineCap:this.lineCap;av.strokeStyle=ah.strokeStyle||this.strokeStyle||"rgba(0,0,0,"+aj+")";av.fillStyle=ah.fillStyle||this.fillStyle||"rgba(0,0,0,"+aj+")";for(var ak=0;ak<an;ak++){var ar=L.jqplot.LinePattern(av,aq);av.translate(Math.cos(this.angle*Math.PI/180)*al,Math.sin(this.angle*Math.PI/180)*al);ar.beginPath();if(aw){av.arc(at[0],at[1],at[2],at[3],at[4],true)}else{if(ap){if(ap){av.fillRect(at[0],at[1],at[2],at[3])}}else{if(at&&at.length){var ai=true;for(var am=0;am<at.length;am++){if(at[am][0]!=null&&at[am][1]!=null){if(ai){ar.moveTo(at[am][0],at[am][1]);ai=false}else{ar.lineTo(at[am][0],at[am][1])}}else{ai=true}}}}}if(ao){ar.closePath()}if(au){av.fill()}else{av.stroke()}}av.restore()};L.jqplot.ShapeRenderer=function(ah){this.lineWidth=1.5;this.linePattern="solid";this.lineJoin="miter";this.lineCap="round";this.closePath=false;this.fill=false;this.isarc=false;this.fillRect=false;this.strokeRect=false;this.clearRect=false;this.strokeStyle="#999999";this.fillStyle="#999999";L.extend(true,this,ah)};L.jqplot.ShapeRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.ShapeRenderer.prototype.draw=function(at,aq,av){at.save();var ah=(av!=null)?av:{};var ar=(ah.fill!=null)?ah.fill:this.fill;var am=(ah.closePath!=null)?ah.closePath:this.closePath;var an=(ah.fillRect!=null)?ah.fillRect:this.fillRect;var ak=(ah.strokeRect!=null)?ah.strokeRect:this.strokeRect;var ai=(ah.clearRect!=null)?ah.clearRect:this.clearRect;var au=(ah.isarc!=null)?ah.isarc:this.isarc;var ao=(ah.linePattern!=null)?ah.linePattern:this.linePattern;var ap=L.jqplot.LinePattern(at,ao);at.lineWidth=ah.lineWidth||this.lineWidth;at.lineJoin=ah.lineJoin||this.lineJoin;at.lineCap=ah.lineCap||this.lineCap;at.strokeStyle=(ah.strokeStyle||ah.color)||this.strokeStyle;at.fillStyle=ah.fillStyle||this.fillStyle;at.beginPath();if(au){at.arc(aq[0],aq[1],aq[2],aq[3],aq[4],true);if(am){at.closePath()}if(ar){at.fill()}else{at.stroke()}at.restore();return}else{if(ai){at.clearRect(aq[0],aq[1],aq[2],aq[3]);at.restore();return}else{if(an||ak){if(an){at.fillRect(aq[0],aq[1],aq[2],aq[3])}if(ak){at.strokeRect(aq[0],aq[1],aq[2],aq[3]);at.restore();return}}else{if(aq&&aq.length){var aj=true;for(var al=0;al<aq.length;al++){if(aq[al][0]!=null&&aq[al][1]!=null){if(aj){ap.moveTo(aq[al][0],aq[al][1]);aj=false}else{ap.lineTo(aq[al][0],aq[al][1])}}else{aj=true}}if(am){ap.closePath()}if(ar){at.fill()}else{at.stroke()}}}}}at.restore()};L.jqplot.TableLegendRenderer=function(){};L.jqplot.TableLegendRenderer.prototype.init=function(ah){L.extend(true,this,ah)};L.jqplot.TableLegendRenderer.prototype.addrow=function(aq,ak,ah,ao){var al=(ah)?this.rowSpacing+"px":"0px";var ap;var aj;var ai;var an;var am;ai=document.createElement("tr");ap=L(ai);ap.addClass("jqplot-table-legend");ai=null;if(ao){ap.prependTo(this._elem)}else{ap.appendTo(this._elem)}if(this.showSwatches){aj=L(document.createElement("td"));aj.addClass("jqplot-table-legend jqplot-table-legend-swatch");aj.css({textAlign:"center",paddingTop:al});an=L(document.createElement("div"));an.addClass("jqplot-table-legend-swatch-outline");am=L(document.createElement("div"));am.addClass("jqplot-table-legend-swatch");am.css({backgroundColor:ak,borderColor:ak});ap.append(aj.append(an.append(am)))}if(this.showLabels){aj=L(document.createElement("td"));aj.addClass("jqplot-table-legend jqplot-table-legend-label");aj.css("paddingTop",al);ap.append(aj);if(this.escapeHtml){aj.text(aq)}else{aj.html(aq)}}aj=null;an=null;am=null;ap=null;ai=null};L.jqplot.TableLegendRenderer.prototype.draw=function(){if(this._elem){this._elem.emptyForce();this._elem=null}if(this.show){var am=this._series;var ai=document.createElement("table");this._elem=L(ai);this._elem.addClass("jqplot-table-legend");var ar={position:"absolute"};if(this.background){ar.background=this.background}if(this.border){ar.border=this.border}if(this.fontSize){ar.fontSize=this.fontSize}if(this.fontFamily){ar.fontFamily=this.fontFamily}if(this.textColor){ar.textColor=this.textColor}if(this.marginTop!=null){ar.marginTop=this.marginTop}if(this.marginBottom!=null){ar.marginBottom=this.marginBottom}if(this.marginLeft!=null){ar.marginLeft=this.marginLeft}if(this.marginRight!=null){ar.marginRight=this.marginRight}var ah=false,ao=false,aq;for(var an=0;an<am.length;an++){aq=am[an];if(aq._stack||aq.renderer.constructor==L.jqplot.BezierCurveRenderer){ao=true}if(aq.show&&aq.showLabel){var al=this.labels[an]||aq.label.toString();if(al){var aj=aq.color;if(ao&&an<am.length-1){ah=true}else{if(ao&&an==am.length-1){ah=false}}this.renderer.addrow.call(this,al,aj,ah,ao);ah=true}for(var ak=0;ak<L.jqplot.addLegendRowHooks.length;ak++){var ap=L.jqplot.addLegendRowHooks[ak].call(this,aq);if(ap){this.renderer.addrow.call(this,ap.label,ap.color,ah);ah=true}}al=null}}}return this._elem};L.jqplot.TableLegendRenderer.prototype.pack=function(aj){if(this.show){if(this.placement=="insideGrid"){switch(this.location){case"nw":var ai=aj.left;var ah=aj.top;this._elem.css("left",ai);this._elem.css("top",ah);break;case"n":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;var ah=aj.top;this._elem.css("left",ai);this._elem.css("top",ah);break;case"ne":var ai=aj.right;var ah=aj.top;this._elem.css({right:ai,top:ah});break;case"e":var ai=aj.right;var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({right:ai,top:ah});break;case"se":var ai=aj.right;var ah=aj.bottom;this._elem.css({right:ai,bottom:ah});break;case"s":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;var ah=aj.bottom;this._elem.css({left:ai,bottom:ah});break;case"sw":var ai=aj.left;var ah=aj.bottom;this._elem.css({left:ai,bottom:ah});break;case"w":var ai=aj.left;var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({left:ai,top:ah});break;default:var ai=aj.right;var ah=aj.bottom;this._elem.css({right:ai,bottom:ah});break}}else{if(this.placement=="outside"){switch(this.location){case"nw":var ai=this._plotDimensions.width-aj.left;var ah=aj.top;this._elem.css("right",ai);this._elem.css("top",ah);break;case"n":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;var ah=this._plotDimensions.height-aj.top;this._elem.css("left",ai);this._elem.css("bottom",ah);break;case"ne":var ai=this._plotDimensions.width-aj.right;var ah=aj.top;this._elem.css({left:ai,top:ah});break;case"e":var ai=this._plotDimensions.width-aj.right;var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({left:ai,top:ah});break;case"se":var ai=this._plotDimensions.width-aj.right;var ah=aj.bottom;this._elem.css({left:ai,bottom:ah});break;case"s":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;var ah=this._plotDimensions.height-aj.bottom;this._elem.css({left:ai,top:ah});break;case"sw":var ai=this._plotDimensions.width-aj.left;var ah=aj.bottom;this._elem.css({right:ai,bottom:ah});break;case"w":var ai=this._plotDimensions.width-aj.left;var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({right:ai,top:ah});break;default:var ai=aj.right;var ah=aj.bottom;this._elem.css({right:ai,bottom:ah});break}}else{switch(this.location){case"nw":this._elem.css({left:0,top:aj.top});break;case"n":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;this._elem.css({left:ai,top:aj.top});break;case"ne":this._elem.css({right:0,top:aj.top});break;case"e":var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({right:aj.right,top:ah});break;case"se":this._elem.css({right:aj.right,bottom:aj.bottom});break;case"s":var ai=(aj.left+(this._plotDimensions.width-aj.right))/2-this.getWidth()/2;this._elem.css({left:ai,bottom:aj.bottom});break;case"sw":this._elem.css({left:aj.left,bottom:aj.bottom});break;case"w":var ah=(aj.top+(this._plotDimensions.height-aj.bottom))/2-this.getHeight()/2;this._elem.css({left:aj.left,top:ah});break;default:this._elem.css({right:aj.right,bottom:aj.bottom});break}}}}};L.jqplot.ThemeEngine=function(){this.themes={};this.activeTheme=null};L.jqplot.ThemeEngine.prototype.init=function(){var ak=new L.jqplot.Theme({_name:"Default"});var an,ai,am;for(an in ak.target){if(an=="textColor"){ak.target[an]=this.target.css("color")}else{ak.target[an]=this.target.css(an)}}if(this.title.show&&this.title._elem){for(an in ak.title){if(an=="textColor"){ak.title[an]=this.title._elem.css("color")}else{ak.title[an]=this.title._elem.css(an)}}}for(an in ak.grid){ak.grid[an]=this.grid[an]}if(ak.grid.backgroundColor==null&&this.grid.background!=null){ak.grid.backgroundColor=this.grid.background}if(this.legend.show&&this.legend._elem){for(an in ak.legend){if(an=="textColor"){ak.legend[an]=this.legend._elem.css("color")}else{ak.legend[an]=this.legend._elem.css(an)}}}var aj;for(ai=0;ai<this.series.length;ai++){aj=this.series[ai];if(aj.renderer.constructor==L.jqplot.LineRenderer){ak.series.push(new p())}else{if(aj.renderer.constructor==L.jqplot.BarRenderer){ak.series.push(new T())}else{if(aj.renderer.constructor==L.jqplot.PieRenderer){ak.series.push(new f())}else{if(aj.renderer.constructor==L.jqplot.DonutRenderer){ak.series.push(new G())}else{if(aj.renderer.constructor==L.jqplot.FunnelRenderer){ak.series.push(new Z())}else{if(aj.renderer.constructor==L.jqplot.MeterGaugeRenderer){ak.series.push(new D())}else{ak.series.push({})}}}}}}for(an in ak.series[ai]){ak.series[ai][an]=aj[an]}}var ah,al;for(an in this.axes){al=this.axes[an];ah=ak.axes[an]=new P();ah.borderColor=al.borderColor;ah.borderWidth=al.borderWidth;if(al._ticks&&al._ticks[0]){for(am in ah.ticks){if(al._ticks[0].hasOwnProperty(am)){ah.ticks[am]=al._ticks[0][am]}else{if(al._ticks[0]._elem){ah.ticks[am]=al._ticks[0]._elem.css(am)}}}}if(al._label&&al._label.show){for(am in ah.label){if(al._label[am]){ah.label[am]=al._label[am]}else{if(al._label._elem){if(am=="textColor"){ah.label[am]=al._label._elem.css("color")}else{ah.label[am]=al._label._elem.css(am)}}}}}}this.themeEngine._add(ak);this.themeEngine.activeTheme=this.themeEngine.themes[ak._name]};L.jqplot.ThemeEngine.prototype.get=function(ah){if(!ah){return this.activeTheme}else{return this.themes[ah]}};function O(ai,ah){return ai-ah}L.jqplot.ThemeEngine.prototype.getThemeNames=function(){var ah=[];for(var ai in this.themes){ah.push(ai)}return ah.sort(O)};L.jqplot.ThemeEngine.prototype.getThemes=function(){var ai=[];var ah=[];for(var ak in this.themes){ai.push(ak)}ai.sort(O);for(var aj=0;aj<ai.length;aj++){ah.push(this.themes[ai[aj]])}return ah};L.jqplot.ThemeEngine.prototype.activate=function(av,aB){var ah=false;if(!aB&&this.activeTheme&&this.activeTheme._name){aB=this.activeTheme._name}if(!this.themes.hasOwnProperty(aB)){throw new Error("No theme of that name")}else{var am=this.themes[aB];this.activeTheme=am;var aA,at=false,ar=false;var ai=["xaxis","x2axis","yaxis","y2axis"];for(aw=0;aw<ai.length;aw++){var an=ai[aw];if(am.axesStyles.borderColor!=null){av.axes[an].borderColor=am.axesStyles.borderColor}if(am.axesStyles.borderWidth!=null){av.axes[an].borderWidth=am.axesStyles.borderWidth}}for(var az in av.axes){var ak=av.axes[az];if(ak.show){var aq=am.axes[az]||{};var ao=am.axesStyles;var al=L.jqplot.extend(true,{},aq,ao);aA=(am.axesStyles.borderColor!=null)?am.axesStyles.borderColor:al.borderColor;if(al.borderColor!=null){ak.borderColor=al.borderColor;ah=true}aA=(am.axesStyles.borderWidth!=null)?am.axesStyles.borderWidth:al.borderWidth;if(al.borderWidth!=null){ak.borderWidth=al.borderWidth;ah=true}if(ak._ticks&&ak._ticks[0]){for(var aj in al.ticks){aA=al.ticks[aj];if(aA!=null){ak.tickOptions[aj]=aA;ak._ticks=[];ah=true}}}if(ak._label&&ak._label.show){for(var aj in al.label){aA=al.label[aj];if(aA!=null){ak.labelOptions[aj]=aA;ah=true}}}}}for(var au in am.grid){if(am.grid[au]!=null){av.grid[au]=am.grid[au]}}if(!ah){av.grid.draw()}if(av.legend.show){for(au in am.legend){if(am.legend[au]!=null){av.legend[au]=am.legend[au]}}}if(av.title.show){for(au in am.title){if(am.title[au]!=null){av.title[au]=am.title[au]}}}var aw;for(aw=0;aw<am.series.length;aw++){var ap={};var ay=false;for(au in am.series[aw]){aA=(am.seriesStyles[au]!=null)?am.seriesStyles[au]:am.series[aw][au];if(aA!=null){ap[au]=aA;if(au=="color"){av.series[aw].renderer.shapeRenderer.fillStyle=aA;av.series[aw].renderer.shapeRenderer.strokeStyle=aA;av.series[aw][au]=aA}else{if((au=="lineWidth")||(au=="linePattern")){av.series[aw].renderer.shapeRenderer[au]=aA;av.series[aw][au]=aA}else{if(au=="markerOptions"){V(av.series[aw].markerOptions,aA);V(av.series[aw].markerRenderer,aA)}else{av.series[aw][au]=aA}}}ah=true}}}if(ah){av.target.empty();av.draw()}for(au in am.target){if(am.target[au]!=null){av.target.css(au,am.target[au])}}}};L.jqplot.ThemeEngine.prototype._add=function(ai,ah){if(ah){ai._name=ah}if(!ai._name){ai._name=Date.parse(new Date())}if(!this.themes.hasOwnProperty(ai._name)){this.themes[ai._name]=ai}else{throw new Error("jqplot.ThemeEngine Error: Theme already in use")}};L.jqplot.ThemeEngine.prototype.remove=function(ah){if(ah=="Default"){return false}return delete this.themes[ah]};L.jqplot.ThemeEngine.prototype.newTheme=function(ah,aj){if(typeof(ah)=="object"){aj=aj||ah;ah=null}if(aj&&aj._name){ah=aj._name}else{ah=ah||Date.parse(new Date())}var ai=this.copy(this.themes.Default._name,ah);L.jqplot.extend(ai,aj);return ai};function B(aj){if(aj==null||typeof(aj)!="object"){return aj}var ah=new aj.constructor();for(var ai in aj){ah[ai]=B(aj[ai])}return ah}L.jqplot.clone=B;function V(aj,ai){if(ai==null||typeof(ai)!="object"){return}for(var ah in ai){if(ah=="highlightColors"){aj[ah]=B(ai[ah])}if(ai[ah]!=null&&typeof(ai[ah])=="object"){if(!aj.hasOwnProperty(ah)){aj[ah]={}}V(aj[ah],ai[ah])}else{aj[ah]=ai[ah]}}}L.jqplot.merge=V;L.jqplot.extend=function(){var am=arguments[0]||{},ak=1,al=arguments.length,ah=false,aj;if(typeof am==="boolean"){ah=am;am=arguments[1]||{};ak=2}if(typeof am!=="object"&&!toString.call(am)==="[object Function]"){am={}}for(;ak<al;ak++){if((aj=arguments[ak])!=null){for(var ai in aj){var an=am[ai],ao=aj[ai];if(am===ao){continue}if(ah&&ao&&typeof ao==="object"&&!ao.nodeType){am[ai]=L.jqplot.extend(ah,an||(ao.length!=null?[]:{}),ao)}else{if(ao!==u){am[ai]=ao}}}}}return am};L.jqplot.ThemeEngine.prototype.rename=function(ai,ah){if(ai=="Default"||ah=="Default"){throw new Error("jqplot.ThemeEngine Error: Cannot rename from/to Default")}if(this.themes.hasOwnProperty(ah)){throw new Error("jqplot.ThemeEngine Error: New name already in use.")}else{if(this.themes.hasOwnProperty(ai)){var aj=this.copy(ai,ah);this.remove(ai);return aj}}throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid")};L.jqplot.ThemeEngine.prototype.copy=function(ah,aj,al){if(aj=="Default"){throw new Error("jqplot.ThemeEngine Error: Cannot copy over Default theme")}if(!this.themes.hasOwnProperty(ah)){var ai="jqplot.ThemeEngine Error: Source name invalid";throw new Error(ai)}if(this.themes.hasOwnProperty(aj)){var ai="jqplot.ThemeEngine Error: Target name invalid";throw new Error(ai)}else{var ak=B(this.themes[ah]);ak._name=aj;L.jqplot.extend(true,ak,al);this._add(ak);return ak}};L.jqplot.Theme=function(ah,ai){if(typeof(ah)=="object"){ai=ai||ah;ah=null}ah=ah||Date.parse(new Date());this._name=ah;this.target={backgroundColor:null};this.legend={textColor:null,fontFamily:null,fontSize:null,border:null,background:null};this.title={textColor:null,fontFamily:null,fontSize:null,textAlign:null};this.seriesStyles={};this.series=[];this.grid={drawGridlines:null,gridLineColor:null,gridLineWidth:null,backgroundColor:null,borderColor:null,borderWidth:null,shadow:null};this.axesStyles={label:{},ticks:{}};this.axes={};if(typeof(ai)=="string"){this._name=ai}else{if(typeof(ai)=="object"){L.jqplot.extend(true,this,ai)}}};var P=function(){this.borderColor=null;this.borderWidth=null;this.ticks=new o();this.label=new t()};var o=function(){this.show=null;this.showGridline=null;this.showLabel=null;this.showMark=null;this.size=null;this.textColor=null;this.whiteSpace=null;this.fontSize=null;this.fontFamily=null};var t=function(){this.textColor=null;this.whiteSpace=null;this.fontSize=null;this.fontFamily=null;this.fontWeight=null};var p=function(){this.color=null;this.lineWidth=null;this.linePattern=null;this.shadow=null;this.fillColor=null;this.showMarker=null;this.markerOptions=new I()};var I=function(){this.show=null;this.style=null;this.lineWidth=null;this.size=null;this.color=null;this.shadow=null};var T=function(){this.color=null;this.seriesColors=null;this.lineWidth=null;this.shadow=null;this.barPadding=null;this.barMargin=null;this.barWidth=null;this.highlightColors=null};var f=function(){this.seriesColors=null;this.padding=null;this.sliceMargin=null;this.fill=null;this.shadow=null;this.startAngle=null;this.lineWidth=null;this.highlightColors=null};var G=function(){this.seriesColors=null;this.padding=null;this.sliceMargin=null;this.fill=null;this.shadow=null;this.startAngle=null;this.lineWidth=null;this.innerDiameter=null;this.thickness=null;this.ringMargin=null;this.highlightColors=null};var Z=function(){this.color=null;this.lineWidth=null;this.shadow=null;this.padding=null;this.sectionMargin=null;this.seriesColors=null;this.highlightColors=null};var D=function(){this.padding=null;this.backgroundColor=null;this.ringColor=null;this.tickColor=null;this.ringWidth=null;this.intervalColors=null;this.intervalInnerRadius=null;this.intervalOuterRadius=null;this.hubRadius=null;this.needleThickness=null;this.needlePad=null};L.fn.jqplotChildText=function(){return L(this).contents().filter(function(){return this.nodeType==3}).text()};L.fn.jqplotGetComputedFontStyle=function(){var ak=window.getComputedStyle?window.getComputedStyle(this[0],""):this[0].currentStyle;var ai=ak["font-style"]?["font-style","font-weight","font-size","font-family"]:["fontStyle","fontWeight","fontSize","fontFamily"];var al=[];for(var aj=0;aj<ai.length;++aj){var ah=String(ak[ai[aj]]);if(ah&&ah!="normal"){al.push(ah)}}return al.join(" ")};L.fn.jqplotToImageCanvas=function(aj){aj=aj||{};var av=(aj.x_offset==null)?0:aj.x_offset;var ax=(aj.y_offset==null)?0:aj.y_offset;var al=(aj.backgroundColor==null)?"rgb(255,255,255)":aj.backgroundColor;if(L(this).width()==0||L(this).height()==0){return null}if(L.jqplot.use_excanvas){return null}var an=document.createElement("canvas");var aA=L(this).outerHeight(true);var at=L(this).outerWidth(true);var am=L(this).offset();var ao=am.left;var aq=am.top;var au=0,ar=0;var ay=["jqplot-table-legend","jqplot-xaxis-tick","jqplot-x2axis-tick","jqplot-yaxis-tick","jqplot-y2axis-tick","jqplot-y3axis-tick","jqplot-y4axis-tick","jqplot-y5axis-tick","jqplot-y6axis-tick","jqplot-y7axis-tick","jqplot-y8axis-tick","jqplot-y9axis-tick","jqplot-xaxis-label","jqplot-x2axis-label","jqplot-yaxis-label","jqplot-y2axis-label","jqplot-y3axis-label","jqplot-y4axis-label","jqplot-y5axis-label","jqplot-y6axis-label","jqplot-y7axis-label","jqplot-y8axis-label","jqplot-y9axis-label"];var ap,ah,ai,aB;for(var az=0;az<ay.length;az++){L(this).find("."+ay[az]).each(function(){ap=L(this).offset().top-aq;ah=L(this).offset().left-ao;aB=ah+L(this).outerWidth(true)+au;ai=ap+L(this).outerHeight(true)+ar;if(ah<-au){at=at-au-ah;au=-ah}if(ap<-ar){aA=aA-ar-ap;ar=-ap}if(aB>at){at=aB}if(ai>aA){aA=ai}})}an.width=at+Number(av);an.height=aA+Number(ax);var ak=an.getContext("2d");ak.save();ak.fillStyle=al;ak.fillRect(0,0,an.width,an.height);ak.restore();ak.translate(au,ar);ak.textAlign="left";ak.textBaseline="top";function aC(aE){var aF=parseInt(L(aE).css("line-height"),10);if(isNaN(aF)){aF=parseInt(L(aE).css("font-size"),10)*1.2}return aF}function aD(aF,aE,aS,aG,aO,aH){var aQ=aC(aF);var aK=L(aF).innerWidth();var aL=L(aF).innerHeight();var aN=aS.split(/\s+/);var aR=aN.length;var aP="";var aM=[];var aU=aO;var aT=aG;for(var aJ=0;aJ<aR;aJ++){aP+=aN[aJ];if(aE.measureText(aP).width>aK){aM.push(aJ);aP="";aJ--}}if(aM.length===0){if(L(aF).css("textAlign")==="center"){aT=aG+(aH-aE.measureText(aP).width)/2-au}aE.fillText(aS,aT,aO)}else{aP=aN.slice(0,aM[0]).join(" ");if(L(aF).css("textAlign")==="center"){aT=aG+(aH-aE.measureText(aP).width)/2-au}aE.fillText(aP,aT,aU);aU+=aQ;for(var aJ=1,aI=aM.length;aJ<aI;aJ++){aP=aN.slice(aM[aJ-1],aM[aJ]).join(" ");if(L(aF).css("textAlign")==="center"){aT=aG+(aH-aE.measureText(aP).width)/2-au}aE.fillText(aP,aT,aU);aU+=aQ}aP=aN.slice(aM[aJ-1],aN.length).join(" ");if(L(aF).css("textAlign")==="center"){aT=aG+(aH-aE.measureText(aP).width)/2-au}aE.fillText(aP,aT,aU)}}function aw(aG,aJ,aE){var aN=aG.tagName.toLowerCase();var aF=L(aG).position();var aK=window.getComputedStyle?window.getComputedStyle(aG,""):aG.currentStyle;var aI=aJ+aF.left+parseInt(aK.marginLeft,10)+parseInt(aK.borderLeftWidth,10)+parseInt(aK.paddingLeft,10);var aL=aE+aF.top+parseInt(aK.marginTop,10)+parseInt(aK.borderTopWidth,10)+parseInt(aK.paddingTop,10);var aM=an.width;if((aN=="div"||aN=="span")&&!L(aG).hasClass("jqplot-highlighter-tooltip")){L(aG).children().each(function(){aw(this,aI,aL)});var aO=L(aG).jqplotChildText();if(aO){ak.font=L(aG).jqplotGetComputedFontStyle();ak.fillStyle=L(aG).css("color");aD(aG,ak,aO,aI,aL,aM)}}else{if(aN==="table"&&L(aG).hasClass("jqplot-table-legend")){ak.strokeStyle=L(aG).css("border-top-color");ak.fillStyle=L(aG).css("background-color");ak.fillRect(aI,aL,L(aG).innerWidth(),L(aG).innerHeight());if(parseInt(L(aG).css("border-top-width"),10)>0){ak.strokeRect(aI,aL,L(aG).innerWidth(),L(aG).innerHeight())}L(aG).find("div.jqplot-table-legend-swatch-outline").each(function(){var aU=L(this);ak.strokeStyle=aU.css("border-top-color");var aQ=aI+aU.position().left;var aR=aL+aU.position().top;ak.strokeRect(aQ,aR,aU.innerWidth(),aU.innerHeight());aQ+=parseInt(aU.css("padding-left"),10);aR+=parseInt(aU.css("padding-top"),10);var aT=aU.innerHeight()-2*parseInt(aU.css("padding-top"),10);var aP=aU.innerWidth()-2*parseInt(aU.css("padding-left"),10);var aS=aU.children("div.jqplot-table-legend-swatch");ak.fillStyle=aS.css("background-color");ak.fillRect(aQ,aR,aP,aT)});L(aG).find("td.jqplot-table-legend-label").each(function(){var aR=L(this);var aP=aI+aR.position().left;var aQ=aL+aR.position().top+parseInt(aR.css("padding-top"),10);ak.font=aR.jqplotGetComputedFontStyle();ak.fillStyle=aR.css("color");aD(aR,ak,aR.text(),aP,aQ,aM)});var aH=null}else{if(aN=="canvas"){ak.drawImage(aG,aI,aL)}}}}L(this).children().each(function(){aw(this,av,ax)});return an};L.fn.jqplotToImageStr=function(ai){var ah=L(this).jqplotToImageCanvas(ai);if(ah){return ah.toDataURL("image/png")}else{return null}};L.fn.jqplotToImageElem=function(ah){var ai=document.createElement("img");var aj=L(this).jqplotToImageStr(ah);ai.src=aj;return ai};L.fn.jqplotToImageElemStr=function(ah){var ai="<img src="+L(this).jqplotToImageStr(ah)+" />";return ai};L.fn.jqplotSaveImage=function(){var ah=L(this).jqplotToImageStr({});if(ah){window.location.href=ah.replace("image/png","image/octet-stream")}};L.fn.jqplotViewImage=function(){var ai=L(this).jqplotToImageElemStr({});var aj=L(this).jqplotToImageStr({});if(ai){var ah=window.open("");ah.document.open("image/png");ah.document.write(ai);ah.document.close();ah=null}};var ag=function(){this.syntax=ag.config.syntax;this._type="jsDate";this.proxy=new Date();this.options={};this.locale=ag.regional.getLocale();this.formatString="";this.defaultCentury=ag.config.defaultCentury;switch(arguments.length){case 0:break;case 1:if(l(arguments[0])=="[object Object]"&&arguments[0]._type!="jsDate"){var aj=this.options=arguments[0];this.syntax=aj.syntax||this.syntax;this.defaultCentury=aj.defaultCentury||this.defaultCentury;this.proxy=ag.createDate(aj.date)}else{this.proxy=ag.createDate(arguments[0])}break;default:var ah=[];for(var ai=0;ai<arguments.length;ai++){ah.push(arguments[ai])}this.proxy=new Date();this.proxy.setFullYear.apply(this.proxy,ah.slice(0,3));if(ah.slice(3).length){this.proxy.setHours.apply(this.proxy,ah.slice(3))}break}};ag.config={defaultLocale:"en",syntax:"perl",defaultCentury:1900};ag.prototype.add=function(aj,ai){var ah=E[ai]||E.day;if(typeof ah=="number"){this.proxy.setTime(this.proxy.getTime()+(ah*aj))}else{ah.add(this,aj)}return this};ag.prototype.clone=function(){return new ag(this.proxy.getTime())};ag.prototype.getUtcOffset=function(){return this.proxy.getTimezoneOffset()*60000};ag.prototype.diff=function(ai,al,ah){ai=new ag(ai);if(ai===null){return null}var aj=E[al]||E.day;if(typeof aj=="number"){var ak=(this.proxy.getTime()-ai.proxy.getTime())/aj}else{var ak=aj.diff(this.proxy,ai.proxy)}return(ah?ak:Math[ak>0?"floor":"ceil"](ak))};ag.prototype.getAbbrDayName=function(){return ag.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]};ag.prototype.getAbbrMonthName=function(){return ag.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]};ag.prototype.getAMPM=function(){return this.proxy.getHours()>=12?"PM":"AM"};ag.prototype.getAmPm=function(){return this.proxy.getHours()>=12?"pm":"am"};ag.prototype.getCentury=function(){return parseInt(this.proxy.getFullYear()/100,10)};ag.prototype.getDate=function(){return this.proxy.getDate()};ag.prototype.getDay=function(){return this.proxy.getDay()};ag.prototype.getDayOfWeek=function(){var ah=this.proxy.getDay();return ah===0?7:ah};ag.prototype.getDayOfYear=function(){var ai=this.proxy;var ah=ai-new Date(""+ai.getFullYear()+"/1/1 GMT");ah+=ai.getTimezoneOffset()*60000;ai=null;return parseInt(ah/60000/60/24,10)+1};ag.prototype.getDayName=function(){return ag.regional[this.locale]["dayNames"][this.proxy.getDay()]};ag.prototype.getFullWeekOfYear=function(){var ak=this.proxy;var ah=this.getDayOfYear();var aj=6-ak.getDay();var ai=parseInt((ah+aj)/7,10);return ai};ag.prototype.getFullYear=function(){return this.proxy.getFullYear()};ag.prototype.getGmtOffset=function(){var ah=this.proxy.getTimezoneOffset()/60;var ai=ah<0?"+":"-";ah=Math.abs(ah);return ai+N(Math.floor(ah),2)+":"+N((ah%1)*60,2)};ag.prototype.getHours=function(){return this.proxy.getHours()};ag.prototype.getHours12=function(){var ah=this.proxy.getHours();return ah>12?ah-12:(ah==0?12:ah)};ag.prototype.getIsoWeek=function(){var ak=this.proxy;var aj=this.getWeekOfYear();var ah=(new Date(""+ak.getFullYear()+"/1/1")).getDay();var ai=aj+(ah>4||ah<=1?0:1);if(ai==53&&(new Date(""+ak.getFullYear()+"/12/31")).getDay()<4){ai=1}else{if(ai===0){ak=new ag(new Date(""+(ak.getFullYear()-1)+"/12/31"));ai=ak.getIsoWeek()}}ak=null;return ai};ag.prototype.getMilliseconds=function(){return this.proxy.getMilliseconds()};ag.prototype.getMinutes=function(){return this.proxy.getMinutes()};ag.prototype.getMonth=function(){return this.proxy.getMonth()};ag.prototype.getMonthName=function(){return ag.regional[this.locale]["monthNames"][this.proxy.getMonth()]};ag.prototype.getMonthNumber=function(){return this.proxy.getMonth()+1};ag.prototype.getSeconds=function(){return this.proxy.getSeconds()};ag.prototype.getShortYear=function(){return this.proxy.getYear()%100};ag.prototype.getTime=function(){return this.proxy.getTime()};ag.prototype.getTimezoneAbbr=function(){return this.proxy.toString().replace(/^.*\(([^)]+)\)$/,"$1")};ag.prototype.getTimezoneName=function(){var ah=/(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString());return ah[1]||ah[2]||"GMT"+this.getGmtOffset()};ag.prototype.getTimezoneOffset=function(){return this.proxy.getTimezoneOffset()};ag.prototype.getWeekOfYear=function(){var ah=this.getDayOfYear();var aj=7-this.getDayOfWeek();var ai=parseInt((ah+aj)/7,10);return ai};ag.prototype.getUnix=function(){return Math.round(this.proxy.getTime()/1000,0)};ag.prototype.getYear=function(){return this.proxy.getYear()};ag.prototype.next=function(ah){ah=ah||"day";return this.clone().add(1,ah)};ag.prototype.set=function(){switch(arguments.length){case 0:this.proxy=new Date();break;case 1:if(l(arguments[0])=="[object Object]"&&arguments[0]._type!="jsDate"){var aj=this.options=arguments[0];this.syntax=aj.syntax||this.syntax;this.defaultCentury=aj.defaultCentury||this.defaultCentury;this.proxy=ag.createDate(aj.date)}else{this.proxy=ag.createDate(arguments[0])}break;default:var ah=[];for(var ai=0;ai<arguments.length;ai++){ah.push(arguments[ai])}this.proxy=new Date();this.proxy.setFullYear.apply(this.proxy,ah.slice(0,3));if(ah.slice(3).length){this.proxy.setHours.apply(this.proxy,ah.slice(3))}break}return this};ag.prototype.setDate=function(ah){this.proxy.setDate(ah);return this};ag.prototype.setFullYear=function(){this.proxy.setFullYear.apply(this.proxy,arguments);return this};ag.prototype.setHours=function(){this.proxy.setHours.apply(this.proxy,arguments);return this};ag.prototype.setMilliseconds=function(ah){this.proxy.setMilliseconds(ah);return this};ag.prototype.setMinutes=function(){this.proxy.setMinutes.apply(this.proxy,arguments);return this};ag.prototype.setMonth=function(){this.proxy.setMonth.apply(this.proxy,arguments);return this};ag.prototype.setSeconds=function(){this.proxy.setSeconds.apply(this.proxy,arguments);return this};ag.prototype.setTime=function(ah){this.proxy.setTime(ah);return this};ag.prototype.setYear=function(){this.proxy.setYear.apply(this.proxy,arguments);return this};ag.prototype.strftime=function(ah){ah=ah||this.formatString||ag.regional[this.locale]["formatString"];return ag.strftime(this,ah,this.syntax)};ag.prototype.toString=function(){return this.proxy.toString()};ag.prototype.toYmdInt=function(){return(this.proxy.getFullYear()*10000)+(this.getMonthNumber()*100)+this.proxy.getDate()};ag.regional={en:{monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],formatString:"%Y-%m-%d %H:%M:%S"},fr:{monthNames:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthNamesShort:["Jan","Fév","Mar","Avr","Mai","Jun","Jul","Aoû","Sep","Oct","Nov","Déc"],dayNames:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],dayNamesShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],formatString:"%Y-%m-%d %H:%M:%S"},de:{monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],formatString:"%Y-%m-%d %H:%M:%S"},es:{monthNames:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthNamesShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],dayNames:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mié","Juv","Vie","Sáb"],formatString:"%Y-%m-%d %H:%M:%S"},ru:{monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],formatString:"%Y-%m-%d %H:%M:%S"},ar:{monthNames:["كانون الثاني","شباط","آذار","نيسان","آذار","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["السبت","الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة"],dayNamesShort:["سبت","أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة"],formatString:"%Y-%m-%d %H:%M:%S"},pt:{monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],formatString:"%Y-%m-%d %H:%M:%S"},"pt-BR":{monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],formatString:"%Y-%m-%d %H:%M:%S"},pl:{monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lut","Mar","Kwi","Maj","Cze","Lip","Sie","Wrz","Paź","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Ni","Pn","Wt","Śr","Cz","Pt","Sb"],formatString:"%Y-%m-%d %H:%M:%S"},nl:{monthNames:["Januari","Februari","Maart","April","Mei","Juni","July","Augustus","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:","["Zondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag","Zaterdag"],dayNamesShort:["Zo","Ma","Di","Wo","Do","Vr","Za"],formatString:"%Y-%m-%d %H:%M:%S"},sv:{monthNames:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],monthNamesShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],dayNames:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],dayNamesShort:["sön","mån","tis","ons","tor","fre","lör"],formatString:"%Y-%m-%d %H:%M:%S"}};ag.regional["en-US"]=ag.regional["en-GB"]=ag.regional.en;ag.regional.getLocale=function(){var ah=ag.config.defaultLocale;if(document&&document.getElementsByTagName("html")&&document.getElementsByTagName("html")[0].lang){ah=document.getElementsByTagName("html")[0].lang;if(!ag.regional.hasOwnProperty(ah)){ah=ag.config.defaultLocale}}return ah};var C=24*60*60*1000;var N=function(ah,ak){ah=String(ah);var ai=ak-ah.length;var aj=String(Math.pow(10,ai)).slice(1);return aj.concat(ah)};var E={millisecond:1,second:1000,minute:60*1000,hour:60*60*1000,day:C,week:7*C,month:{add:function(aj,ah){E.year.add(aj,Math[ah>0?"floor":"ceil"](ah/12));var ai=aj.getMonth()+(ah%12);if(ai==12){ai=0;aj.setYear(aj.getFullYear()+1)}else{if(ai==-1){ai=11;aj.setYear(aj.getFullYear()-1)}}aj.setMonth(ai)},diff:function(al,aj){var ah=al.getFullYear()-aj.getFullYear();var ai=al.getMonth()-aj.getMonth()+(ah*12);var ak=al.getDate()-aj.getDate();return ai+(ak/30)}},year:{add:function(ai,ah){ai.setYear(ai.getFullYear()+Math[ah>0?"floor":"ceil"](ah))},diff:function(ai,ah){return E.month.diff(ai,ah)/12}}};for(var Y in E){if(Y.substring(Y.length-1)!="s"){E[Y+"s"]=E[Y]}}var H=function(al,ak,ai){if(ag.formats[ai]["shortcuts"][ak]){return ag.strftime(al,ag.formats[ai]["shortcuts"][ak],ai)}else{var ah=(ag.formats[ai]["codes"][ak]||"").split(".");var aj=al["get"+ah[0]]?al["get"+ah[0]]():"";if(ah[1]){aj=N(aj,ah[1])}return aj}};ag.strftime=function(an,ak,aj,ao){var ai="perl";var am=ag.regional.getLocale();if(aj&&ag.formats.hasOwnProperty(aj)){ai=aj}else{if(aj&&ag.regional.hasOwnProperty(aj)){am=aj}}if(ao&&ag.formats.hasOwnProperty(ao)){ai=ao}else{if(ao&&ag.regional.hasOwnProperty(ao)){am=ao}}if(l(an)!="[object Object]"||an._type!="jsDate"){an=new ag(an);an.locale=am}if(!ak){ak=an.formatString||ag.regional[am]["formatString"]}var ah=ak||"%Y-%m-%d",ap="",al;while(ah.length>0){if(al=ah.match(ag.formats[ai].codes.matcher)){ap+=ah.slice(0,al.index);ap+=(al[1]||"")+H(an,al[2],ai);ah=ah.slice(al.index+al[0].length)}else{ap+=ah;ah=""}}return ap};ag.formats={ISO:"%Y-%m-%dT%H:%M:%S.%N%G",SQL:"%Y-%m-%d %H:%M:%S"};ag.formats.perl={codes:{matcher:/()%(#?(%|[a-z]))/i,Y:"FullYear",y:"ShortYear.2",m:"MonthNumber.2","#m":"MonthNumber",B:"MonthName",b:"AbbrMonthName",d:"Date.2","#d":"Date",e:"Date",A:"DayName",a:"AbbrDayName",w:"Day",H:"Hours.2","#H":"Hours",I:"Hours12.2","#I":"Hours12",p:"AMPM",M:"Minutes.2","#M":"Minutes",S:"Seconds.2","#S":"Seconds",s:"Unix",N:"Milliseconds.3","#N":"Milliseconds",O:"TimezoneOffset",Z:"TimezoneName",G:"GmtOffset"},shortcuts:{F:"%Y-%m-%d",T:"%H:%M:%S",X:"%H:%M:%S",x:"%m/%d/%y",D:"%m/%d/%y","#c":"%a %b %e %H:%M:%S %Y",v:"%e-%b-%Y",R:"%H:%M",r:"%I:%M:%S %p",t:"\t",n:"\n","%":"%"}};ag.formats.php={codes:{matcher:/()%((%|[a-z]))/i,a:"AbbrDayName",A:"DayName",d:"Date.2",e:"Date",j:"DayOfYear.3",u:"DayOfWeek",w:"Day",U:"FullWeekOfYear.2",V:"IsoWeek.2",W:"WeekOfYear.2",b:"AbbrMonthName",B:"MonthName",m:"MonthNumber.2",h:"AbbrMonthName",C:"Century.2",y:"ShortYear.2",Y:"FullYear",H:"Hours.2",I:"Hours12.2",l:"Hours12",p:"AMPM",P:"AmPm",M:"Minutes.2",S:"Seconds.2",s:"Unix",O:"TimezoneOffset",z:"GmtOffset",Z:"TimezoneAbbr"},shortcuts:{D:"%m/%d/%y",F:"%Y-%m-%d",T:"%H:%M:%S",X:"%H:%M:%S",x:"%m/%d/%y",R:"%H:%M",r:"%I:%M:%S %p",t:"\t",n:"\n","%":"%"}};ag.createDate=function(aj){if(aj==null){return new Date()}if(aj instanceof Date){return aj}if(typeof aj=="number"){return new Date(aj)}var ao=String(aj).replace(/^\s*(.+)\s*$/g,"$1");ao=ao.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/,"$1/$2/$3");ao=ao.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i,"$1 $2 $3");var an=ao.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i);if(an&&an.length>3){var at=parseFloat(an[3]);var am=ag.config.defaultCentury+at;am=String(am);ao=ao.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i,an[1]+" "+an[2]+" "+am)}an=ao.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/);function ar(ax,aw){var aC=parseFloat(aw[1]);var aB=parseFloat(aw[2]);var aA=parseFloat(aw[3]);var az=ag.config.defaultCentury;var av,au,aD,ay;if(aC>31){au=aA;aD=aB;av=az+aC}else{au=aB;aD=aC;av=az+aA}ay=aD+"/"+au+"/"+av;return ax.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/,ay)}if(an&&an.length>3){ao=ar(ao,an)}var an=ao.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/);if(an&&an.length>3){ao=ar(ao,an)}var al=0;var ai=ag.matchers.length;var aq,ah,ap=ao,ak;while(al<ai){ah=Date.parse(ap);if(!isNaN(ah)){return new Date(ah)}aq=ag.matchers[al];if(typeof aq=="function"){ak=aq.call(ag,ap);if(ak instanceof Date){return ak}}else{ap=ao.replace(aq[0],aq[1])}al++}return NaN};ag.daysInMonth=function(ah,ai){if(ai==2){return new Date(ah,1,29).getDate()==29?29:28}return[u,31,u,31,30,31,30,31,31,30,31,30,31][ai]};ag.matchers=[[/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/,"$2/$1/$3"],[/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/,"$2/$3/$1"],function(ak){var ai=ak.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i);if(ai){if(ai[1]){var aj=this.createDate(ai[1]);if(isNaN(aj)){return}}else{var aj=new Date();aj.setMilliseconds(0)}var ah=parseFloat(ai[2]);if(ai[6]){ah=ai[6].toLowerCase()=="am"?(ah==12?0:ah):(ah==12?12:ah+12)}aj.setHours(ah,parseInt(ai[3]||0,10),parseInt(ai[4]||0,10),((parseFloat(ai[5]||0))||0)*1000);return aj}else{return ak}},function(ak){var ai=ak.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i);if(ai){if(ai[1]){var aj=this.createDate(ai[1]);if(isNaN(aj)){return}}else{var aj=new Date();aj.setMilliseconds(0)}var ah=parseFloat(ai[2]);aj.setHours(ah,parseInt(ai[3],10),parseInt(ai[4],10),parseFloat(ai[5])*1000);return aj}else{return ak}},function(al){var aj=al.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/);if(aj){var ak=new Date();var am=ag.config.defaultCentury;var ao=parseFloat(aj[1]);var an=parseFloat(aj[3]);var ai,ah,ap;if(ao>31){ah=an;ai=am+ao}else{ah=ao;ai=am+an}var ap=ab(aj[2],ag.regional[ag.regional.getLocale()]["monthNamesShort"]);if(ap==-1){ap=ab(aj[2],ag.regional[ag.regional.getLocale()]["monthNames"])}ak.setFullYear(ai,ap,ah);ak.setHours(0,0,0,0);return ak}else{return al}}];function ab(aj,ak){if(ak.indexOf){return ak.indexOf(aj)}for(var ah=0,ai=ak.length;ah<ai;ah++){if(ak[ah]===aj){return ah}}return -1}function l(ah){if(ah===null){return"[object Null]"}return Object.prototype.toString.call(ah)}L.jsDate=ag;L.jqplot.sprintf=function(){function an(au,ap,aq,at){var ar=(au.length>=ap)?"":Array(1+ap-au.length>>>0).join(aq);return at?au+ar:ar+au}function ak(ar){var aq=new String(ar);for(var ap=10;ap>0;ap--){if(aq==(aq=aq.replace(/^(\d+)(\d{3})/,"$1"+L.jqplot.sprintf.thousandsSeparator+"$2"))){break}}return aq}function aj(av,au,ax,ar,at,aq){var aw=ar-av.length;if(aw>0){var ap=" ";if(aq){ap=" "}if(ax||!at){av=an(av,ar,ap,ax)}else{av=av.slice(0,au.length)+an("",aw,"0",true)+av.slice(au.length)}}return av}function ao(ay,aq,aw,ar,ap,av,ax,au){var at=ay>>>0;aw=aw&&at&&{"2":"0b","8":"0","16":"0x"}[aq]||"";ay=aw+an(at.toString(aq),av||0,"0",false);return aj(ay,aw,ar,ap,ax,au)}function ah(au,av,ar,ap,at,aq){if(ap!=null){au=au.slice(0,ap)}return aj(au,"",av,ar,at,aq)}var ai=arguments,al=0,am=ai[al++];return am.replace(L.jqplot.sprintf.regex,function(aM,ax,ay,aB,aO,aJ,av){if(aM=="%%"){return"%"}var aD=false,az="",aA=false,aL=false,aw=false,au=false;for(var aI=0;ay&&aI<ay.length;aI++){switch(ay.charAt(aI)){case" ":az=" ";break;case"+":az="+";break;case"-":aD=true;break;case"0":aA=true;break;case"#":aL=true;break;case"&":aw=true;break;case"'":au=true;break}}if(!aB){aB=0}else{if(aB=="*"){aB=+ai[al++]}else{if(aB.charAt(0)=="*"){aB=+ai[aB.slice(1,-1)]}else{aB=+aB}}}if(aB<0){aB=-aB;aD=true}if(!isFinite(aB)){throw new Error("$.jqplot.sprintf: (minimum-)width must be finite")}if(!aJ){aJ="fFeE".indexOf(av)>-1?6:(av=="d")?0:void (0)}else{if(aJ=="*"){aJ=+ai[al++]}else{if(aJ.charAt(0)=="*"){aJ=+ai[aJ.slice(1,-1)]}else{aJ=+aJ}}}var aF=ax?ai[ax.slice(0,-1)]:ai[al++];switch(av){case"s":if(aF==null){return""}return ah(String(aF),aD,aB,aJ,aA,aw);case"c":return ah(String.fromCharCode(+aF),aD,aB,aJ,aA,aw);case"b":return ao(aF,2,aL,aD,aB,aJ,aA,aw);case"o":return ao(aF,8,aL,aD,aB,aJ,aA,aw);case"x":return ao(aF,16,aL,aD,aB,aJ,aA,aw);case"X":return ao(aF,16,aL,aD,aB,aJ,aA,aw).toUpperCase();case"u":return ao(aF,10,aL,aD,aB,aJ,aA,aw);case"i":var ar=parseInt(+aF,10);if(isNaN(ar)){return""}var aH=ar<0?"-":az;var aK=au?ak(String(Math.abs(ar))):String(Math.abs(ar));aF=aH+an(aK,aJ,"0",false);return aj(aF,aH,aD,aB,aA,aw);case"d":var ar=Math.round(+aF);if(isNaN(ar)){return""}var aH=ar<0?"-":az;var aK=au?ak(String(Math.abs(ar))):String(Math.abs(ar));aF=aH+an(aK,aJ,"0",false);return aj(aF,aH,aD,aB,aA,aw);case"e":case"E":case"f":case"F":case"g":case"G":var ar=+aF;if(isNaN(ar)){return""}var aH=ar<0?"-":az;var at=["toExponential","toFixed","toPrecision"]["efg".indexOf(av.toLowerCase())];var aN=["toString","toUpperCase"]["eEfFgG".indexOf(av)%2];var aK=Math.abs(ar)[at](aJ);var aE=aK.toString().split(".");aE[0]=au?ak(aE[0]):aE[0];aK=aE.join(L.jqplot.sprintf.decimalMark);aF=aH+aK;var aC=aj(aF,aH,aD,aB,aA,aw)[aN]();return aC;case"p":case"P":var ar=+aF;if(isNaN(ar)){return""}var aH=ar<0?"-":az;var aE=String(Number(Math.abs(ar)).toExponential()).split(/e|E/);var aq=(aE[0].indexOf(".")!=-1)?aE[0].length-1:String(ar).length;var aG=(aE[1]<0)?-aE[1]-1:0;if(Math.abs(ar)<1){if(aq+aG<=aJ){aF=aH+Math.abs(ar).toPrecision(aq)}else{if(aq<=aJ-1){aF=aH+Math.abs(ar).toExponential(aq-1)}else{aF=aH+Math.abs(ar).toExponential(aJ-1)}}}else{var ap=(aq<=aJ)?aq:aJ;aF=aH+Math.abs(ar).toPrecision(ap)}var aN=["toString","toUpperCase"]["pP".indexOf(av)%2];return aj(aF,aH,aD,aB,aA,aw)[aN]();case"n":return"";default:return aM}})};L.jqplot.sprintf.thousandsSeparator=",";L.jqplot.sprintf.decimalMark=".";L.jqplot.sprintf.regex=/%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g;L.jqplot.getSignificantFigures=function(al){var an=String(Number(Math.abs(al)).toExponential()).split(/e|E/);var am=(an[0].indexOf(".")!=-1)?an[0].length-1:an[0].length;var ai=(an[1]<0)?-an[1]-1:0;var ah=parseInt(an[1],10);var aj=(ah+1>0)?ah+1:0;var ak=(am<=aj)?0:am-ah-1;return{significantDigits:am,digitsLeft:aj,digitsRight:ak,zeros:ai,exponent:ah}};L.jqplot.getPrecision=function(ah){return L.jqplot.getSignificantFigures(ah).digitsRight};var X=L.uiBackCompat!==false;L.jqplot.effects={effect:{}};var m="jqplot.storage.";L.extend(L.jqplot.effects,{version:"1.9pre",save:function(ai,aj){for(var ah=0;ah<aj.length;ah++){if(aj[ah]!==null){ai.data(m+aj[ah],ai[0].style[aj[ah]])}}},restore:function(ai,aj){for(var ah=0;ah<aj.length;ah++){if(aj[ah]!==null){ai.css(aj[ah],ai.data(m+aj[ah]))}}},setMode:function(ah,ai){if(ai==="toggle"){ai=ah.is(":hidden")?"show":"hide"}return ai},createWrapper:function(ai){if(ai.parent().is(".ui-effects-wrapper")){return ai.parent()}var aj={width:ai.outerWidth(true),height:ai.outerHeight(true),"float":ai.css("float")},al=L("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),ah={width:ai.width(),height:ai.height()},ak=document.activeElement;ai.wrap(al);if(ai[0]===ak||L.contains(ai[0],ak)){L(ak).focus()}al=ai.parent();if(ai.css("position")==="static"){al.css({position:"relative"});ai.css({position:"relative"})}else{L.extend(aj,{position:ai.css("position"),zIndex:ai.css("z-index")});L.each(["top","left","bottom","right"],function(am,an){aj[an]=ai.css(an);if(isNaN(parseInt(aj[an],10))){aj[an]="auto"}});ai.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}ai.css(ah);return al.css(aj).show()},removeWrapper:function(ah){var ai=document.activeElement;if(ah.parent().is(".ui-effects-wrapper")){ah.parent().replaceWith(ah);if(ah[0]===ai||L.contains(ah[0],ai)){L(ai).focus()}}return ah}});function j(ai,ah,aj,ak){if(L.isPlainObject(ai)){return ai}ai={effect:ai};if(ah===u){ah={}}if(L.isFunction(ah)){ak=ah;aj=null;ah={}}if(L.type(ah)==="number"||L.fx.speeds[ah]){ak=aj;aj=ah;ah={}}if(L.isFunction(aj)){ak=aj;aj=null}if(ah){L.extend(ai,ah)}aj=aj||ah.duration;ai.duration=L.fx.off?0:typeof aj==="number"?aj:aj in L.fx.speeds?L.fx.speeds[aj]:L.fx.speeds._default;ai.complete=ak||ah.complete;return ai}function ae(ah){if(!ah||typeof ah==="number"||L.fx.speeds[ah]){return true}if(typeof ah==="string"&&!L.jqplot.effects.effect[ah]){if(X&&L.jqplot.effects[ah]){return false}return true}return false}L.fn.extend({jqplotEffect:function(ap,aq,ai,ao){var an=j.apply(this,arguments),ak=an.mode,al=an.queue,am=L.jqplot.effects.effect[an.effect],ah=!am&&X&&L.jqplot.effects[an.effect];if(L.fx.off||!(am||ah)){if(ak){return this[ak](an.duration,an.complete)}else{return this.each(function(){if(an.complete){an.complete.call(this)}})}}function aj(au){var av=L(this),at=an.complete,aw=an.mode;function ar(){if(L.isFunction(at)){at.call(av[0])}if(L.isFunction(au)){au()}}if(av.is(":hidden")?aw==="hide":aw==="show"){ar()}else{am.call(av[0],an,ar)}}if(am){return al===false?this.each(aj):this.queue(al||"fx",aj)}else{return ah.call(this,{options:an,duration:an.duration,callback:an.complete,mode:an.mode})}}});var a=/up|down|vertical/,v=/up|left|vertical|horizontal/;L.jqplot.effects.effect.blind=function(aj,ao){var ak=L(this),ar=["position","top","bottom","left","right","height","width"],ap=L.jqplot.effects.setMode(ak,aj.mode||"hide"),au=aj.direction||"up",am=a.test(au),al=am?"height":"width",aq=am?"top":"left",aw=v.test(au),an={},av=ap==="show",ai,ah,at;if(ak.parent().is(".ui-effects-wrapper")){L.jqplot.effects.save(ak.parent(),ar)}else{L.jqplot.effects.save(ak,ar)}ak.show();at=parseInt(ak.css("top"),10);ai=L.jqplot.effects.createWrapper(ak).css({overflow:"hidden"});ah=am?ai[al]()+at:ai[al]();an[al]=av?String(ah):"0";if(!aw){ak.css(am?"bottom":"right",0).css(am?"top":"left","").css({position:"absolute"});an[aq]=av?"0":String(ah)}if(av){ai.css(al,0);if(!aw){ai.css(aq,ah)}}ai.animate(an,{duration:aj.duration,easing:aj.easing,queue:false,complete:function(){if(ap==="hide"){ak.hide()}L.jqplot.effects.restore(ak,ar);L.jqplot.effects.removeWrapper(ak);ao()}})}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/optionsTutorial.txt b/web/extensions/jqplot/optionsTutorial.txt new file mode 100644 index 0000000..a8d847d --- /dev/null +++ b/web/extensions/jqplot/optionsTutorial.txt @@ -0,0 +1,243 @@ +Title: Options Tutorial + +This document will help you understand how jqPlot's options +relate to the API documentation and the jqPlot object +itself. For a listing of options available to jqPlot, +see <jqPlot Options> in the jqPlotOptions.txt file. + +The key to effectively using jqPlot is understanding jqPlot's +options. The online documentation is API documentation. While +it explains what attributes and methods various objects possess, +it doesn't explain how to use or set those attributes through +options. This tutorial will help explain that. + +Let's assume you are creating a plot +like this: + +> chart = $.jqplot('chart', dataSeries, optionsObj); + +First, note that you shouldn't try to directly set attributes on the +"chart" object (like chart.grid.shadow) after your call to $.jqplot(). +At best this won't do anything **(see below). You should pass options in via +the "optionsObj". + +The optionsObj really represents the plot object (jqPlot object, not +to be confused with the $.jqplot function which will create a jqPlot +object). Attributes you specify on that object will be merged with +attributes in the jqPlot object. The axes, legend, series, etc. are +attributes on the jqPlot object. The jqPlot/optionsObj object looks +something like (only some attributes shown): + +> jqPlot-| +> |-seriesColors +> |-textColor +> |-fontFamily +> |-fontSize +> |-stackSeries +> |-series(Array)-| +> | |-Series1-| +> | | |-lineWidth +> | | |-linePattern +> | | |-shadow +> | | |-showLine +> | | |-showMarker +> | | |-color +> | |-Series2... +> | |-... +> | |-SeriesN +> | +> |-grid(Object)-| +> | |-drawGridLines +> | |-background +> | |-borderColor +> | |-borderWidth +> | |-shadow +> | +> |-title(Object)-| +> | |-text +> | |-show +> | |-fontFamily +> | |-fontSize +> | |-textAlign +> | |-textColor +> | +> |-axes(Object)-| +> | |-xais-| +> | | |-min +> | | |-max +> | | |-numberTicks +> | | |-showTicks +> | | |-showTickMarks +> | | |-pad +> | +> | ... and so on + +The optionsObj should follow the same construction as if it were a +jqPlot object (with some exceptions/shortcuts I'll mention in a +moment). So generally, when you see something like +"this.drawGridLines" in the grid properties in the docs, just replace +"this" with "grid" in your options object. So it becomes +optionsObj.grid.drawGridLines. Do likewise with the other objects in +the plot, replacing "this", with the respective attribute on the plot +like "legend" or "title". Series and Axes are handled a little +differently, because series is an array and axes has 4 distinct children +"xaxis", "yaxis", "x2axis" and "y2axis". + +So, to remove the shadow from the grid and change the grid border size +you would do: + +> optionObj = {grid:{shadow:false, borderWidth:9.0}}; + +To do the same as above but also make all the text in the plot red you +would do: + +> optionObj = { +> textColor:"#ff0000", +> grid:{shadow:false, borderWidth:9.0} +> } + +Here is a more deeply nested example. Say you want to specify a min +and max on your y axis and use a specific color for your second +series. That would look like: + +> optionsObj = { +> axes:{yaxis:{min:5, max:230}}, +> series:[{},{color:"#33ff66"}] +> } + +Note that series options are an array in order of the series data you +sent in to your plot. To get to the second series, you have to put an +object (even if empty) in place of the first series. + +There is a handy shortcut to assign options to all axes or all series +at one go. Use axesDefaults and seriesDefaults. So, if you wanted +both x and y axes to start at 0 and you wanted all series to not show +markers, you could do: + +> optionsObj = {axesDefaults:{min:0}, seriesDefaults:{showMarker:false}} + +Another shortcut is for the plot title. Normally, you would assign +options to the title as an object. If you specify a title option as a +string, it will assign that to the title.text property automatically. +So these two are equivalent: + +> optionsObj = {title:{text:"My Plot"}} + +and + +> optionsObj = {title:"My Plot"} + +Where things need more explanation is with renderers, plugins and +their options. Briefly, what's the difference between a renderer and +a plugin. + +A renderer is an object that is used to draw something and gets +attached to an existing object in the plot in order to draw it. A +plugin does more than just provide drawing functionality to an +object; it can calculate a trend line, change the +cursor, provide event driven functionality, etc. I consider renderers +plugins, but plugins don't have to be renderers. + +So, how do you use renderers and plugins, and specify their options? +Some common renderers are for bar charts and category axes. If you +want to render your series as a bar chart with each set of bars +showing up in a category on the x axis, you do: + +> optionsObj = { +> seriesDefaults:{renderer:$.jqplot.BarRenderer}, +> axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}} +> } + +This replaces the default renderer used for all series in the plot +with a bar renderer and the x axis default renderer (but not any other +axis) with a category renderer. + +Now, how would I assign options to those renderers? The renderer's +attributes may not be present in the pre-existing jqPlot object, they +may be specific to the renderer. This is done through the +"rendererOptions" option on the appropriate object. So, if I wanted my +bars to be 25 pixels wide, I would do: + + +> optionsObj = { +> seriesDefaults:{ +> renderer:$.jqplot.BarRenderer}, +> rendererOptions:{ +> barWidth:25 +> }, +> axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}} +> } + +Again, this is using the "seriesDefaults" option, which will apply +options to all series in the plot. You could do the same on any +particular series in the plot through the "series" options array. + +Plugins are free to add their own options. For example, the +highlighter plugin has its own set of options that are unique to it. +As a result, it responds to options placed in the "highlighter" +attribute of your options object. So, if I wanted to change the +highlighter tooltip to fade in and out slowly and be positioned +directly above the point I'm highlighting: + +> optionsObj = { +> highlighter:{tooltipFadeSpeed:'slow', tooltipLocation:'n'} +> } + +Other plugins, like dragable and trendlines, add their options in with +the series. (Yes, that's the correct name for the dragable plugin; it +doesn't use the correct spelling of "draggable".) +This is because both of those plugins can have different +options for different series in the plot. So, if you wanted to specify the +color for the dragable plugin and constrain it to drag only on the x axis as well +as specify the color of the trend line you could do: + +> series:[{ +> dragable: { +> color: '#ff3366', +> constrainTo: 'x' +> }, +> trendline: { +> color: '#cccccc' +> } +> }] + +This would apply those options to the first series only. If you had 2 series +and wanted to turn off dragging and trend lines on the second series, you could do: + +> series:[{ +> dragable: { +> color: '#ff3366', +> constrainTo: 'x' +> }, +> trendline: { +> color: '#cccccc' +> } +> }, { +> isDragable: false, +> trendline:{ +> show: false +> } +> }] + +Note, series draggability is turned off with the "isDragable" option directly on +the series itself, not with a suboption of "dragable". This may be improved +in the future. + +I hope this is helpful. +A few key points to remember: + +- When you see "this" in the api docs, you generally replace it with +the name of the object (in lowercase) you are looking at in your +options object. +- seriesDefaults and axesDefaults are convenient shortcuts. +- to assign options to a renderer, generally use the "rendererOptions" +- plugins may add their own options attribute, like "highlighter" or +"cursor". + +** Note: you can set attributes after the plot is created (like +plot.grid.shadow = false), but you'll have to issue the appropriate +calls to possibly reinitialize and redraw the plot. jqPlot can +definitely handle this to change the plot after creation (this is how +the dragable plugin updates the plot data and the trend line plugin +recomputes itself when data changes). This hasn't been documented +yet, however. diff --git a/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.js b/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.js new file mode 100644 index 0000000..1c7b031 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.js @@ -0,0 +1,314 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + // Class: $.jqplot.BezierCurveRenderer.js + // Renderer which draws lines as stacked bezier curves. + // Data for the line will not be specified as an array of + // [x, y] data point values, but as a an array of [start piont, bezier curve] + // So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]]. + $.jqplot.BezierCurveRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer; + + + // Method: setGridData + // converts the user data values to grid coordinates and stores them + // in the gridData array. + // Called with scope of a series. + $.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + // this._plotData should be same as this.data + var data = this.data; + this.gridData = []; + this._prevGridData = []; + // if seriesIndex = 0, fill to x axis. + // if seriesIndex > 0, fill to previous series data. + var idx = this.index; + if (data.length == 2) { + if (idx == 0) { + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], + [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + else { + if (idx == 0) { + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], + [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + }; + + // Method: makeGridData + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var gd = []; + var pgd = []; + // if seriesIndex = 0, fill to x axis. + // if seriesIndex > 0, fill to previous series data. + var idx = this.index; + if (data.length == 2) { + if (idx == 0) { + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], + [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + else { + if (idx == 0) { + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], + [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + return gd; + }; + + + // called within scope of series. + $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) { + var i; + ctx.save(); + if (gd.length) { + if (this.showLine) { + ctx.save(); + var opts = (options != null) ? options : {}; + ctx.fillStyle = opts.fillStyle || this.color; + ctx.beginPath(); + ctx.moveTo(gd[0][0], gd[0][1]); + ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]); + ctx.lineTo(gd[2][0], gd[2][1]); + if (gd[3].length == 2) { + ctx.lineTo(gd[3][0], gd[3][1]); + } + else { + ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]); + } + ctx.closePath(); + ctx.fill(); + ctx.restore(); + } + } + + ctx.restore(); + }; + + $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) { + // This is a no-op, shadows drawn with lines. + }; + + $.jqplot.BezierAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer; + + + // Axes on a plot with Bezier Curves + $.jqplot.BezierAxisRenderer.prototype.init = function(options){ + $.extend(true, this, options); + var db = this._dataBounds; + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + var d = s.data; + if (d.length == 4) { + for (var j=0; j<d.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + if (d[j][0] < db.min || db.min == null) { + db.min = d[j][0]; + } + if (d[j][0] > db.max || db.max == null) { + db.max = d[j][0]; + } + } + else { + if (d[j][1] < db.min || db.min == null) { + db.min = d[j][1]; + } + if (d[j][1] > db.max || db.max == null) { + db.max = d[j][1]; + } + } + } + } + else { + if (this.name == 'xaxis' || this.name == 'x2axis') { + if (d[0][0] < db.min || db.min == null) { + db.min = d[0][0]; + } + if (d[0][0] > db.max || db.max == null) { + db.max = d[0][0]; + } + for (var j=0; j<5; j+=2) { + if (d[1][j] < db.min || db.min == null) { + db.min = d[1][j]; + } + if (d[1][j] > db.max || db.max == null) { + db.max = d[1][j]; + } + } + } + else { + if (d[0][1] < db.min || db.min == null) { + db.min = d[0][1]; + } + if (d[0][1] > db.max || db.max == null) { + db.max = d[0][1]; + } + for (var j=1; j<6; j+=2) { + if (d[1][j] < db.min || db.min == null) { + db.min = d[1][j]; + } + if (d[1][j] > db.max || db.max == null) { + db.max = d[1][j]; + } + } + } + } + } + }; + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults); + options.seriesDefaults = options.seriesDefaults || {}; + options.legend = $.extend(true, {placement:'outside'}, options.legend); + // only set these if there is a pie series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer; + } + } + + $.jqplot.preInitHooks.push(preInit); + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.min.js new file mode 100644 index 0000000..94c6fc5 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.BezierCurveRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(b){b.jqplot.BezierCurveRenderer=function(){b.jqplot.LineRenderer.call(this)};b.jqplot.BezierCurveRenderer.prototype=new b.jqplot.LineRenderer();b.jqplot.BezierCurveRenderer.prototype.constructor=b.jqplot.BezierCurveRenderer;b.jqplot.BezierCurveRenderer.prototype.setGridData=function(h){var e=this._xaxis.series_u2p;var g=this._yaxis.series_u2p;var f=this.data;this.gridData=[];this._prevGridData=[];var d=this.index;if(f.length==2){if(d==0){this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[1][2]),g.call(this._yaxis,f[1][3]),e.call(this._xaxis,f[1][4]),g.call(this._yaxis,f[1][5])],[e.call(this._xaxis,f[1][4]),g.call(this._yaxis,this._yaxis.min)],[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,this._yaxis.min)]]}else{var c=h.series[d-1].data;this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[1][2]),g.call(this._yaxis,f[1][3]),e.call(this._xaxis,f[1][4]),g.call(this._yaxis,f[1][5])],[e.call(this._xaxis,c[1][4]),g.call(this._yaxis,c[1][5])],[e.call(this._xaxis,c[1][2]),g.call(this._yaxis,c[1][3]),e.call(this._xaxis,c[1][0]),g.call(this._yaxis,c[1][1]),e.call(this._xaxis,c[0][0]),g.call(this._yaxis,c[0][1])]]}}else{if(d==0){this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[2][0]),g.call(this._yaxis,f[2][1]),e.call(this._xaxis,f[3][0]),g.call(this._yaxis,f[3][1])],[e.call(this._xaxis,f[3][1]),g.call(this._yaxis,this._yaxis.min)],[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,this._yaxis.min)]]}else{var c=h.series[d-1].data;this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[2][0]),g.call(this._yaxis,f[2][1]),e.call(this._xaxis,f[3][0]),g.call(this._yaxis,f[3][1])],[e.call(this._xaxis,c[3][0]),g.call(this._yaxis,c[3][1])],[e.call(this._xaxis,c[2][0]),g.call(this._yaxis,c[2][1]),e.call(this._xaxis,c[1][0]),g.call(this._yaxis,c[1][1]),e.call(this._xaxis,c[0][0]),g.call(this._yaxis,c[0][1])]]}}};b.jqplot.BezierCurveRenderer.prototype.makeGridData=function(g,i){var f=this._xaxis.series_u2p;var h=this._yaxis.series_u2p;var e=[];var j=[];var d=this.index;if(g.length==2){if(d==0){e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[1][2]),h.call(this._yaxis,g[1][3]),f.call(this._xaxis,g[1][4]),h.call(this._yaxis,g[1][5])],[f.call(this._xaxis,g[1][4]),h.call(this._yaxis,this._yaxis.min)],[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,this._yaxis.min)]]}else{var c=i.series[d-1].data;e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[1][2]),h.call(this._yaxis,g[1][3]),f.call(this._xaxis,g[1][4]),h.call(this._yaxis,g[1][5])],[f.call(this._xaxis,c[1][4]),h.call(this._yaxis,c[1][5])],[f.call(this._xaxis,c[1][2]),h.call(this._yaxis,c[1][3]),f.call(this._xaxis,c[1][0]),h.call(this._yaxis,c[1][1]),f.call(this._xaxis,c[0][0]),h.call(this._yaxis,c[0][1])]]}}else{if(d==0){e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[2][0]),h.call(this._yaxis,g[2][1]),f.call(this._xaxis,g[3][0]),h.call(this._yaxis,g[3][1])],[f.call(this._xaxis,g[3][1]),h.call(this._yaxis,this._yaxis.min)],[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,this._yaxis.min)]]}else{var c=i.series[d-1].data;e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[2][0]),h.call(this._yaxis,g[2][1]),f.call(this._xaxis,g[3][0]),h.call(this._yaxis,g[3][1])],[f.call(this._xaxis,c[3][0]),h.call(this._yaxis,c[3][1])],[f.call(this._xaxis,c[2][0]),h.call(this._yaxis,c[2][1]),f.call(this._xaxis,c[1][0]),h.call(this._yaxis,c[1][1]),f.call(this._xaxis,c[0][0]),h.call(this._yaxis,c[0][1])]]}}return e};b.jqplot.BezierCurveRenderer.prototype.draw=function(c,g,d){var e;c.save();if(g.length){if(this.showLine){c.save();var f=(d!=null)?d:{};c.fillStyle=f.fillStyle||this.color;c.beginPath();c.moveTo(g[0][0],g[0][1]);c.bezierCurveTo(g[1][0],g[1][1],g[1][2],g[1][3],g[1][4],g[1][5]);c.lineTo(g[2][0],g[2][1]);if(g[3].length==2){c.lineTo(g[3][0],g[3][1])}else{c.bezierCurveTo(g[3][0],g[3][1],g[3][2],g[3][3],g[3][4],g[3][5])}c.closePath();c.fill();c.restore()}}c.restore()};b.jqplot.BezierCurveRenderer.prototype.drawShadow=function(c,e,d){};b.jqplot.BezierAxisRenderer=function(){b.jqplot.LinearAxisRenderer.call(this)};b.jqplot.BezierAxisRenderer.prototype=new b.jqplot.LinearAxisRenderer();b.jqplot.BezierAxisRenderer.prototype.constructor=b.jqplot.BezierAxisRenderer;b.jqplot.BezierAxisRenderer.prototype.init=function(f){b.extend(true,this,f);var c=this._dataBounds;for(var g=0;g<this._series.length;g++){var h=this._series[g];var k=h.data;if(k.length==4){for(var e=0;e<k.length;e++){if(this.name=="xaxis"||this.name=="x2axis"){if(k[e][0]<c.min||c.min==null){c.min=k[e][0]}if(k[e][0]>c.max||c.max==null){c.max=k[e][0]}}else{if(k[e][1]<c.min||c.min==null){c.min=k[e][1]}if(k[e][1]>c.max||c.max==null){c.max=k[e][1]}}}}else{if(this.name=="xaxis"||this.name=="x2axis"){if(k[0][0]<c.min||c.min==null){c.min=k[0][0]}if(k[0][0]>c.max||c.max==null){c.max=k[0][0]}for(var e=0;e<5;e+=2){if(k[1][e]<c.min||c.min==null){c.min=k[1][e]}if(k[1][e]>c.max||c.max==null){c.max=k[1][e]}}}else{if(k[0][1]<c.min||c.min==null){c.min=k[0][1]}if(k[0][1]>c.max||c.max==null){c.max=k[0][1]}for(var e=1;e<6;e+=2){if(k[1][e]<c.min||c.min==null){c.min=k[1][e]}if(k[1][e]>c.max||c.max==null){c.max=k[1][e]}}}}}};function a(g,f,d){d=d||{};d.axesDefaults=b.extend(true,{pad:0},d.axesDefaults);d.seriesDefaults=d.seriesDefaults||{};d.legend=b.extend(true,{placement:"outside"},d.legend);var c=false;if(d.seriesDefaults.renderer==b.jqplot.BezierCurveRenderer){c=true}else{if(d.series){for(var e=0;e<d.series.length;e++){if(d.series[e].renderer==b.jqplot.BezierCurveRenderer){c=true}}}}if(c){d.axesDefaults.renderer=b.jqplot.BezierAxisRenderer}}b.jqplot.preInitHooks.push(a)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.barRenderer.js b/web/extensions/jqplot/plugins/jqplot.barRenderer.js new file mode 100644 index 0000000..9b714e9 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.barRenderer.js @@ -0,0 +1,801 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + // Class: $.jqplot.BarRenderer + // A plugin renderer for jqPlot to draw a bar plot. + // Draws series as a line. + + $.jqplot.BarRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer; + + // called with scope of series. + $.jqplot.BarRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + // prop: barPadding + // Number of pixels between adjacent bars at the same axis value. + this.barPadding = 8; + // prop: barMargin + // Number of pixels between groups of bars at adjacent axis values. + this.barMargin = 10; + // prop: barDirection + // 'vertical' = up and down bars, 'horizontal' = side to side bars + this.barDirection = 'vertical'; + // prop: barWidth + // Width of the bar in pixels (auto by devaul). null = calculated automatically. + this.barWidth = null; + // prop: shadowOffset + // offset of the shadow from the slice and offset of + // each succesive stroke of the shadow from the last. + this.shadowOffset = 2; + // prop: shadowDepth + // number of strokes to apply to the shadow, + // each stroke offset shadowOffset from the last. + this.shadowDepth = 5; + // prop: shadowAlpha + // transparency of the shadow (0 = transparent, 1 = opaque) + this.shadowAlpha = 0.08; + // prop: waterfall + // true to enable waterfall plot. + this.waterfall = false; + // prop: groups + // group bars into this many groups + this.groups = 1; + // prop: varyBarColor + // true to color each bar of a series separately rather than + // have every bar of a given series the same color. + // If used for non-stacked multiple series bar plots, user should + // specify a separate 'seriesColors' array for each series. + // Otherwise, each series will set their bars to the same color array. + // This option has no Effect for stacked bar charts and is disabled. + this.varyBarColor = false; + // prop: highlightMouseOver + // True to highlight slice when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a slice. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a slice. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // an array of colors to use when highlighting a bar. + this.highlightColors = []; + // prop: transposedData + // NOT IMPLEMENTED YET. True if this is a horizontal bar plot and + // x and y values are "transposed". Tranposed, or "swapped", data is + // required prior to rev. 894 builds of jqPlot with horizontal bars. + // Allows backward compatability of bar renderer horizontal bars with + // old style data sets. + this.transposedData = true; + this.renderer.animation = { + show: false, + direction: 'down', + speed: 3000, + _supported: true + }; + this._type = 'bar'; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + ////// + // This is probably wrong here. + // After going back and forth on whether renderer should be the thing + // or extend the thing, it seems that it it best if it is a property + // on the thing. This should be something that is commonized + // among series renderers in the future. + ////// + $.extend(true, this, options); + + // really should probably do this + $.extend(true, this.renderer, options); + // fill is still needed to properly draw the legend. + // bars have to be filled. + this.fill = true; + + // if horizontal bar and animating, reset the default direction + if (this.barDirection === 'horizontal' && this.rendererOptions.animation && this.rendererOptions.animation.direction == null) { + this.renderer.animation.direction = 'left'; + } + + if (this.waterfall) { + this.fillToZero = false; + this.disableStack = true; + } + + if (this.barDirection == 'vertical' ) { + this._primaryAxis = '_xaxis'; + this._stackAxis = 'y'; + this.fillAxis = 'y'; + } + else { + this._primaryAxis = '_yaxis'; + this._stackAxis = 'x'; + this.fillAxis = 'x'; + } + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + // total number of values for all bar series, total number of bar series, and position of this series + this._plotSeriesInfo = null; + // Array of actual data colors used for each data point. + this._dataColors = []; + this._barPoints = []; + + // set the shape renderer options + var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill}; + this.renderer.shapeRenderer.init(opts); + // set the shadow renderer options + var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill}; + this.renderer.shadowRenderer.init(sopts); + + plot.postInitHooks.addOnce(postInit); + plot.postDrawHooks.addOnce(postPlotDraw); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + }; + + // called with scope of series + function barPreInit(target, data, seriesDefaults, options) { + if (this.rendererOptions.barDirection == 'horizontal') { + this._stackAxis = 'x'; + this._primaryAxis = '_yaxis'; + } + if (this.rendererOptions.waterfall == true) { + this._data = $.extend(true, [], this.data); + var sum = 0; + var pos = (!this.rendererOptions.barDirection || this.rendererOptions.barDirection === 'vertical' || this.transposedData === false) ? 1 : 0; + for(var i=0; i<this.data.length; i++) { + sum += this.data[i][pos]; + if (i>0) { + this.data[i][pos] += this.data[i-1][pos]; + } + } + this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1]; + this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1]; + } + if (this.rendererOptions.groups > 1) { + this.breakOnNull = true; + var l = this.data.length; + var skip = parseInt(l/this.rendererOptions.groups, 10); + var count = 0; + for (var i=skip; i<l; i+=skip) { + this.data.splice(i+count, 0, [null, null]); + this._plotData.splice(i+count, 0, [null, null]); + this._stackData.splice(i+count, 0, [null, null]); + count++; + } + for (i=0; i<this.data.length; i++) { + if (this._primaryAxis == '_xaxis') { + this.data[i][0] = i+1; + this._plotData[i][0] = i+1; + this._stackData[i][0] = i+1; + } + else { + this.data[i][1] = i+1; + this._plotData[i][1] = i+1; + this._stackData[i][1] = i+1; + } + } + } + } + + $.jqplot.preSeriesInitHooks.push(barPreInit); + + // needs to be called with scope of series, not renderer. + $.jqplot.BarRenderer.prototype.calcSeriesNumbers = function() { + var nvals = 0; + var nseries = 0; + var paxis = this[this._primaryAxis]; + var s, series, pos; + // loop through all series on this axis + for (var i=0; i < paxis._series.length; i++) { + series = paxis._series[i]; + if (series === this) { + pos = i; + } + // is the series rendered as a bar? + if (series.renderer.constructor == $.jqplot.BarRenderer) { + // gridData may not be computed yet, use data length insted + nvals += series.data.length; + nseries += 1; + } + } + // return total number of values for all bar series, total number of bar series, and position of this series + return [nvals, nseries, pos]; + }; + + $.jqplot.BarRenderer.prototype.setBarWidth = function() { + // need to know how many data values we have on the approprate axis and figure it out. + var i; + var nvals = 0; + var nseries = 0; + var paxis = this[this._primaryAxis]; + var s, series, pos; + var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); + nvals = temp[0]; + nseries = temp[1]; + var nticks = paxis.numberTicks; + var nbins = (nticks-1)/2; + // so, now we have total number of axis values. + if (paxis.name == 'xaxis' || paxis.name == 'x2axis') { + if (this._stack) { + this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin; + } + else { + this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries; + // this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries; + } + } + else { + if (this._stack) { + this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin; + } + else { + this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries; + // this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries; + } + } + return [nvals, nseries]; + }; + + function computeHighlightColors (colors) { + var ret = []; + for (var i=0; i<colors.length; i++){ + var rgba = $.jqplot.getColorComponents(colors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); + } + return ret; + } + + function getStart(sidx, didx, comp, plot, axis) { + // check if sign change + var seriesIndex = sidx, + prevSeriesIndex = sidx - 1, + start, + prevVal, + aidx = (axis === 'x') ? 0 : 1; + + // is this not the first series? + if (seriesIndex > 0) { + prevVal = plot.series[prevSeriesIndex]._plotData[didx][aidx]; + + // is there a sign change + if ((comp * prevVal) < 0) { + start = getStart(prevSeriesIndex, didx, comp, plot, axis); + } + + // no sign change. + else { + start = plot.series[prevSeriesIndex].gridData[didx][aidx]; + } + + } + + // if first series, return value at 0 + else { + + start = (aidx === 0) ? plot.series[seriesIndex]._xaxis.series_u2p(0) : plot.series[seriesIndex]._yaxis.series_u2p(0); + } + + return start; + } + + + $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options, plot) { + var i; + // Ughhh, have to make a copy of options b/c it may be modified later. + var opts = $.extend({}, options); + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var xaxis = this.xaxis; + var yaxis = this.yaxis; + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var pointx, pointy; + // clear out data colors. + this._dataColors = []; + this._barPoints = []; + + if (this.barWidth == null) { + this.renderer.setBarWidth.call(this); + } + + var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); + var nvals = temp[0]; + var nseries = temp[1]; + var pos = temp[2]; + var points = []; + + if (this._stack) { + this._barNudge = 0; + } + else { + this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding); + } + if (showLine) { + var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors); + var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors); + var negativeColor = negativeColors.get(this.index); + if (! this.useNegativeColors) { + negativeColor = opts.fillStyle; + } + var positiveColor = opts.fillStyle; + var base; + var xstart; + var ystart; + + if (this.barDirection == 'vertical') { + for (var i=0; i<gridData.length; i++) { + if (!this._stack && this.data[i][1] == null) { + continue; + } + points = []; + base = gridData[i][0] + this._barNudge; + + // stacked + if (this._stack && this._prevGridData.length) { + ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y'); + } + + // not stacked + else { + if (this.fillToZero) { + ystart = this._yaxis.series_u2p(0); + } + else if (this.waterfall && i > 0 && i < this.gridData.length-1) { + ystart = this.gridData[i-1][1]; + } + else if (this.waterfall && i == 0 && i < this.gridData.length-1) { + if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { + ystart = this._yaxis.series_u2p(0); + } + else if (this._yaxis.min > 0) { + ystart = ctx.canvas.height; + } + else { + ystart = 0; + } + } + else if (this.waterfall && i == this.gridData.length - 1) { + if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { + ystart = this._yaxis.series_u2p(0); + } + else if (this._yaxis.min > 0) { + ystart = ctx.canvas.height; + } + else { + ystart = 0; + } + } + else { + ystart = ctx.canvas.height; + } + } + if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { + if (this.varyBarColor && !this._stack) { + if (this.useNegativeColors) { + opts.fillStyle = negativeColors.next(); + } + else { + opts.fillStyle = positiveColors.next(); + } + } + else { + opts.fillStyle = negativeColor; + } + } + else { + if (this.varyBarColor && !this._stack) { + opts.fillStyle = positiveColors.next(); + } + else { + opts.fillStyle = positiveColor; + } + } + + if (!this.fillToZero || this._plotData[i][1] >= 0) { + points.push([base-this.barWidth/2, ystart]); + points.push([base-this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, ystart]); + } + // for negative bars make sure points are always ordered clockwise + else { + points.push([base-this.barWidth/2, gridData[i][1]]); + points.push([base-this.barWidth/2, ystart]); + points.push([base+this.barWidth/2, ystart]); + points.push([base+this.barWidth/2, gridData[i][1]]); + } + this._barPoints.push(points); + // now draw the shadows if not stacked. + // for stacked plots, they are predrawn by drawShadow + if (shadow && !this._stack) { + var sopts = $.extend(true, {}, opts); + // need to get rid of fillStyle on shadow. + delete sopts.fillStyle; + this.renderer.shadowRenderer.draw(ctx, points, sopts); + } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); + this.renderer.shapeRenderer.draw(ctx, points, opts); + } + } + + else if (this.barDirection == 'horizontal'){ + for (var i=0; i<gridData.length; i++) { + if (!this._stack && this.data[i][0] == null) { + continue; + } + points = []; + base = gridData[i][1] - this._barNudge; + xstart; + + if (this._stack && this._prevGridData.length) { + xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x'); + } + // not stacked + else { + if (this.fillToZero) { + xstart = this._xaxis.series_u2p(0); + } + else if (this.waterfall && i > 0 && i < this.gridData.length-1) { + xstart = this.gridData[i-1][0]; + } + else if (this.waterfall && i == 0 && i < this.gridData.length-1) { + if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { + xstart = this._xaxis.series_u2p(0); + } + else if (this._xaxis.min > 0) { + xstart = 0; + } + else { + xstart = 0; + } + } + else if (this.waterfall && i == this.gridData.length - 1) { + if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { + xstart = this._xaxis.series_u2p(0); + } + else if (this._xaxis.min > 0) { + xstart = 0; + } + else { + xstart = ctx.canvas.width; + } + } + else { + xstart = 0; + } + } + if ((this.fillToZero && this._plotData[i][0] < 0) || (this.waterfall && this._data[i][0] < 0)) { + if (this.varyBarColor && !this._stack) { + if (this.useNegativeColors) { + opts.fillStyle = negativeColors.next(); + } + else { + opts.fillStyle = positiveColors.next(); + } + } + else { + opts.fillStyle = negativeColor; + } + } + else { + if (this.varyBarColor && !this._stack) { + opts.fillStyle = positiveColors.next(); + } + else { + opts.fillStyle = positiveColor; + } + } + + + if (!this.fillToZero || this._plotData[i][0] >= 0) { + points.push([xstart, base + this.barWidth / 2]); + points.push([xstart, base - this.barWidth / 2]); + points.push([gridData[i][0], base - this.barWidth / 2]); + points.push([gridData[i][0], base + this.barWidth / 2]); + } + else { + points.push([gridData[i][0], base + this.barWidth / 2]); + points.push([gridData[i][0], base - this.barWidth / 2]); + points.push([xstart, base - this.barWidth / 2]); + points.push([xstart, base + this.barWidth / 2]); + } + + this._barPoints.push(points); + // now draw the shadows if not stacked. + // for stacked plots, they are predrawn by drawShadow + if (shadow && !this._stack) { + var sopts = $.extend(true, {}, opts); + delete sopts.fillStyle; + this.renderer.shadowRenderer.draw(ctx, points, sopts); + } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); + this.renderer.shapeRenderer.draw(ctx, points, opts); + } + } + } + + if (this.highlightColors.length == 0) { + this.highlightColors = $.jqplot.computeHighlightColors(this._dataColors); + } + + else if (typeof(this.highlightColors) == 'string') { + var temp = this.highlightColors; + this.highlightColors = []; + for (var i=0; i<this._dataColors.length; i++) { + this.highlightColors.push(temp); + } + } + + }; + + + // for stacked plots, shadows will be pre drawn by drawShadow. + $.jqplot.BarRenderer.prototype.drawShadow = function(ctx, gridData, options, plot) { + var i; + var opts = (options != undefined) ? options : {}; + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var xaxis = this.xaxis; + var yaxis = this.yaxis; + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var pointx, points, pointy, nvals, nseries, pos; + + if (this._stack && this.shadow) { + if (this.barWidth == null) { + this.renderer.setBarWidth.call(this); + } + + var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); + nvals = temp[0]; + nseries = temp[1]; + pos = temp[2]; + + if (this._stack) { + this._barNudge = 0; + } + else { + this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding); + } + if (showLine) { + + if (this.barDirection == 'vertical') { + for (var i=0; i<gridData.length; i++) { + if (this.data[i][1] == null) { + continue; + } + points = []; + var base = gridData[i][0] + this._barNudge; + var ystart; + + if (this._stack && this._prevGridData.length) { + ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y'); + } + else { + if (this.fillToZero) { + ystart = this._yaxis.series_u2p(0); + } + else { + ystart = ctx.canvas.height; + } + } + + points.push([base-this.barWidth/2, ystart]); + points.push([base-this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, ystart]); + this.renderer.shadowRenderer.draw(ctx, points, opts); + } + } + + else if (this.barDirection == 'horizontal'){ + for (var i=0; i<gridData.length; i++) { + if (this.data[i][0] == null) { + continue; + } + points = []; + var base = gridData[i][1] - this._barNudge; + var xstart; + + if (this._stack && this._prevGridData.length) { + xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x'); + } + else { + if (this.fillToZero) { + xstart = this._xaxis.series_u2p(0); + } + else { + xstart = 0; + } + } + + points.push([xstart, base+this.barWidth/2]); + points.push([gridData[i][0], base+this.barWidth/2]); + points.push([gridData[i][0], base-this.barWidth/2]); + points.push([xstart, base-this.barWidth/2]); + this.renderer.shadowRenderer.draw(ctx, points, opts); + } + } + } + + } + }; + + function postInit(target, data, options) { + for (var i=0; i<this.series.length; i++) { + if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) { + // don't allow mouseover and mousedown at same time. + if (this.series[i].highlightMouseOver) { + this.series[i].highlightMouseDown = false; + } + } + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.barRenderer && this.plugins.barRenderer.highlightCanvas) { + + this.plugins.barRenderer.highlightCanvas.resetCanvas(); + this.plugins.barRenderer.highlightCanvas = null; + } + + this.plugins.barRenderer = {highlightedSeriesIndex:null}; + this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions, this)); + this.plugins.barRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + function highlight (plot, sidx, pidx, points) { + var s = plot.series[sidx]; + var canvas = plot.plugins.barRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.barRenderer.highlightedSeriesIndex = sidx; + var opts = {fillStyle: s.highlightColors[pidx]}; + s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); + canvas = null; + } + + function unhighlight (plot) { + var canvas = plot.plugins.barRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.barRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + canvas = null; + } + + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].show && plot.series[ins[0]].highlightMouseOver && + !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.barRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var idx = plot.plugins.barRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.barRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.barRenderer.min.js new file mode 100644 index 0000000..93d7a45 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.barRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(d){d.jqplot.BarRenderer=function(){d.jqplot.LineRenderer.call(this)};d.jqplot.BarRenderer.prototype=new d.jqplot.LineRenderer();d.jqplot.BarRenderer.prototype.constructor=d.jqplot.BarRenderer;d.jqplot.BarRenderer.prototype.init=function(o,q){this.barPadding=8;this.barMargin=10;this.barDirection="vertical";this.barWidth=null;this.shadowOffset=2;this.shadowDepth=5;this.shadowAlpha=0.08;this.waterfall=false;this.groups=1;this.varyBarColor=false;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.transposedData=true;this.renderer.animation={show:false,direction:"down",speed:3000,_supported:true};this._type="bar";if(o.highlightMouseDown&&o.highlightMouseOver==null){o.highlightMouseOver=false}d.extend(true,this,o);d.extend(true,this.renderer,o);this.fill=true;if(this.barDirection==="horizontal"&&this.rendererOptions.animation&&this.rendererOptions.animation.direction==null){this.renderer.animation.direction="left"}if(this.waterfall){this.fillToZero=false;this.disableStack=true}if(this.barDirection=="vertical"){this._primaryAxis="_xaxis";this._stackAxis="y";this.fillAxis="y"}else{this._primaryAxis="_yaxis";this._stackAxis="x";this.fillAxis="x"}this._highlightedPoint=null;this._plotSeriesInfo=null;this._dataColors=[];this._barPoints=[];var p={lineJoin:"miter",lineCap:"round",fill:true,isarc:false,strokeStyle:this.color,fillStyle:this.color,closePath:this.fill};this.renderer.shapeRenderer.init(p);var n={lineJoin:"miter",lineCap:"round",fill:true,isarc:false,angle:this.shadowAngle,offset:this.shadowOffset,alpha:this.shadowAlpha,depth:this.shadowDepth,closePath:this.fill};this.renderer.shadowRenderer.init(n);q.postInitHooks.addOnce(h);q.postDrawHooks.addOnce(j);q.eventListenerHooks.addOnce("jqplotMouseMove",b);q.eventListenerHooks.addOnce("jqplotMouseDown",a);q.eventListenerHooks.addOnce("jqplotMouseUp",l);q.eventListenerHooks.addOnce("jqplotClick",e);q.eventListenerHooks.addOnce("jqplotRightClick",m)};function g(t,p,o,w){if(this.rendererOptions.barDirection=="horizontal"){this._stackAxis="x";this._primaryAxis="_yaxis"}if(this.rendererOptions.waterfall==true){this._data=d.extend(true,[],this.data);var s=0;var u=(!this.rendererOptions.barDirection||this.rendererOptions.barDirection==="vertical"||this.transposedData===false)?1:0;for(var q=0;q<this.data.length;q++){s+=this.data[q][u];if(q>0){this.data[q][u]+=this.data[q-1][u]}}this.data[this.data.length]=(u==1)?[this.data.length+1,s]:[s,this.data.length+1];this._data[this._data.length]=(u==1)?[this._data.length+1,s]:[s,this._data.length+1]}if(this.rendererOptions.groups>1){this.breakOnNull=true;var n=this.data.length;var v=parseInt(n/this.rendererOptions.groups,10);var r=0;for(var q=v;q<n;q+=v){this.data.splice(q+r,0,[null,null]);this._plotData.splice(q+r,0,[null,null]);this._stackData.splice(q+r,0,[null,null]);r++}for(q=0;q<this.data.length;q++){if(this._primaryAxis=="_xaxis"){this.data[q][0]=q+1;this._plotData[q][0]=q+1;this._stackData[q][0]=q+1}else{this.data[q][1]=q+1;this._plotData[q][1]=q+1;this._stackData[q][1]=q+1}}}}d.jqplot.preSeriesInitHooks.push(g);d.jqplot.BarRenderer.prototype.calcSeriesNumbers=function(){var r=0;var t=0;var q=this[this._primaryAxis];var p,o,u;for(var n=0;n<q._series.length;n++){o=q._series[n];if(o===this){u=n}if(o.renderer.constructor==d.jqplot.BarRenderer){r+=o.data.length;t+=1}}return[r,t,u]};d.jqplot.BarRenderer.prototype.setBarWidth=function(){var q;var n=0;var o=0;var t=this[this._primaryAxis];var x,r,v;var w=this._plotSeriesInfo=this.renderer.calcSeriesNumbers.call(this);n=w[0];o=w[1];var u=t.numberTicks;var p=(u-1)/2;if(t.name=="xaxis"||t.name=="x2axis"){if(this._stack){this.barWidth=(t._offsets.max-t._offsets.min)/n*o-this.barMargin}else{this.barWidth=((t._offsets.max-t._offsets.min)/p-this.barPadding*(o-1)-this.barMargin*2)/o}}else{if(this._stack){this.barWidth=(t._offsets.min-t._offsets.max)/n*o-this.barMargin}else{this.barWidth=((t._offsets.min-t._offsets.max)/p-this.barPadding*(o-1)-this.barMargin*2)/o}}return[n,o]};function f(o){var q=[];for(var s=0;s<o.length;s++){var r=d.jqplot.getColorComponents(o[s]);var n=[r[0],r[1],r[2]];var t=n[0]+n[1]+n[2];for(var p=0;p<3;p++){n[p]=(t>570)?n[p]*0.8:n[p]+0.3*(255-n[p]);n[p]=parseInt(n[p],10)}q.push("rgb("+n[0]+","+n[1]+","+n[2]+")")}return q}function i(v,u,s,t,o){var q=v,w=v-1,n,p,r=(o==="x")?0:1;if(q>0){p=t.series[w]._plotData[u][r];if((s*p)<0){n=i(w,u,s,t,o)}else{n=t.series[w].gridData[u][r]}}else{n=(r===0)?t.series[q]._xaxis.series_u2p(0):t.series[q]._yaxis.series_u2p(0)}return n}d.jqplot.BarRenderer.prototype.draw=function(E,L,q,G){var I;var A=d.extend({},q);var w=(A.shadow!=undefined)?A.shadow:this.shadow;var O=(A.showLine!=undefined)?A.showLine:this.showLine;var F=(A.fill!=undefined)?A.fill:this.fill;var p=this.xaxis;var J=this.yaxis;var y=this._xaxis.series_u2p;var K=this._yaxis.series_u2p;var D,C;this._dataColors=[];this._barPoints=[];if(this.barWidth==null){this.renderer.setBarWidth.call(this)}var N=this._plotSeriesInfo=this.renderer.calcSeriesNumbers.call(this);var x=N[0];var v=N[1];var s=N[2];var H=[];if(this._stack){this._barNudge=0}else{this._barNudge=(-Math.abs(v/2-0.5)+s)*(this.barWidth+this.barPadding)}if(O){var u=new d.jqplot.ColorGenerator(this.negativeSeriesColors);var B=new d.jqplot.ColorGenerator(this.seriesColors);var M=u.get(this.index);if(!this.useNegativeColors){M=A.fillStyle}var t=A.fillStyle;var r;var P;var o;if(this.barDirection=="vertical"){for(var I=0;I<L.length;I++){if(!this._stack&&this.data[I][1]==null){continue}H=[];r=L[I][0]+this._barNudge;if(this._stack&&this._prevGridData.length){o=i(this.index,I,this._plotData[I][1],G,"y")}else{if(this.fillToZero){o=this._yaxis.series_u2p(0)}else{if(this.waterfall&&I>0&&I<this.gridData.length-1){o=this.gridData[I-1][1]}else{if(this.waterfall&&I==0&&I<this.gridData.length-1){if(this._yaxis.min<=0&&this._yaxis.max>=0){o=this._yaxis.series_u2p(0)}else{if(this._yaxis.min>0){o=E.canvas.height}else{o=0}}}else{if(this.waterfall&&I==this.gridData.length-1){if(this._yaxis.min<=0&&this._yaxis.max>=0){o=this._yaxis.series_u2p(0)}else{if(this._yaxis.min>0){o=E.canvas.height}else{o=0}}}else{o=E.canvas.height}}}}}if((this.fillToZero&&this._plotData[I][1]<0)||(this.waterfall&&this._data[I][1]<0)){if(this.varyBarColor&&!this._stack){if(this.useNegativeColors){A.fillStyle=u.next()}else{A.fillStyle=B.next()}}else{A.fillStyle=M}}else{if(this.varyBarColor&&!this._stack){A.fillStyle=B.next()}else{A.fillStyle=t}}if(!this.fillToZero||this._plotData[I][1]>=0){H.push([r-this.barWidth/2,o]);H.push([r-this.barWidth/2,L[I][1]]);H.push([r+this.barWidth/2,L[I][1]]);H.push([r+this.barWidth/2,o])}else{H.push([r-this.barWidth/2,L[I][1]]);H.push([r-this.barWidth/2,o]);H.push([r+this.barWidth/2,o]);H.push([r+this.barWidth/2,L[I][1]])}this._barPoints.push(H);if(w&&!this._stack){var z=d.extend(true,{},A);delete z.fillStyle;this.renderer.shadowRenderer.draw(E,H,z)}var n=A.fillStyle||this.color;this._dataColors.push(n);this.renderer.shapeRenderer.draw(E,H,A)}}else{if(this.barDirection=="horizontal"){for(var I=0;I<L.length;I++){if(!this._stack&&this.data[I][0]==null){continue}H=[];r=L[I][1]-this._barNudge;P;if(this._stack&&this._prevGridData.length){P=i(this.index,I,this._plotData[I][0],G,"x")}else{if(this.fillToZero){P=this._xaxis.series_u2p(0)}else{if(this.waterfall&&I>0&&I<this.gridData.length-1){P=this.gridData[I-1][0]}else{if(this.waterfall&&I==0&&I<this.gridData.length-1){if(this._xaxis.min<=0&&this._xaxis.max>=0){P=this._xaxis.series_u2p(0)}else{if(this._xaxis.min>0){P=0}else{P=0}}}else{if(this.waterfall&&I==this.gridData.length-1){if(this._xaxis.min<=0&&this._xaxis.max>=0){P=this._xaxis.series_u2p(0)}else{if(this._xaxis.min>0){P=0}else{P=E.canvas.width}}}else{P=0}}}}}if((this.fillToZero&&this._plotData[I][0]<0)||(this.waterfall&&this._data[I][0]<0)){if(this.varyBarColor&&!this._stack){if(this.useNegativeColors){A.fillStyle=u.next()}else{A.fillStyle=B.next()}}else{A.fillStyle=M}}else{if(this.varyBarColor&&!this._stack){A.fillStyle=B.next()}else{A.fillStyle=t}}if(!this.fillToZero||this._plotData[I][0]>=0){H.push([P,r+this.barWidth/2]);H.push([P,r-this.barWidth/2]);H.push([L[I][0],r-this.barWidth/2]);H.push([L[I][0],r+this.barWidth/2])}else{H.push([L[I][0],r+this.barWidth/2]);H.push([L[I][0],r-this.barWidth/2]);H.push([P,r-this.barWidth/2]);H.push([P,r+this.barWidth/2])}this._barPoints.push(H);if(w&&!this._stack){var z=d.extend(true,{},A);delete z.fillStyle;this.renderer.shadowRenderer.draw(E,H,z)}var n=A.fillStyle||this.color;this._dataColors.push(n);this.renderer.shapeRenderer.draw(E,H,A)}}}}if(this.highlightColors.length==0){this.highlightColors=d.jqplot.computeHighlightColors(this._dataColors)}else{if(typeof(this.highlightColors)=="string"){var N=this.highlightColors;this.highlightColors=[];for(var I=0;I<this._dataColors.length;I++){this.highlightColors.push(N)}}}};d.jqplot.BarRenderer.prototype.drawShadow=function(z,G,p,B){var D;var w=(p!=undefined)?p:{};var t=(w.shadow!=undefined)?w.shadow:this.shadow;var I=(w.showLine!=undefined)?w.showLine:this.showLine;var A=(w.fill!=undefined)?w.fill:this.fill;var o=this.xaxis;var E=this.yaxis;var v=this._xaxis.series_u2p;var F=this._yaxis.series_u2p;var y,C,x,u,s,r;if(this._stack&&this.shadow){if(this.barWidth==null){this.renderer.setBarWidth.call(this)}var H=this._plotSeriesInfo=this.renderer.calcSeriesNumbers.call(this);u=H[0];s=H[1];r=H[2];if(this._stack){this._barNudge=0}else{this._barNudge=(-Math.abs(s/2-0.5)+r)*(this.barWidth+this.barPadding)}if(I){if(this.barDirection=="vertical"){for(var D=0;D<G.length;D++){if(this.data[D][1]==null){continue}C=[];var q=G[D][0]+this._barNudge;var n;if(this._stack&&this._prevGridData.length){n=i(this.index,D,this._plotData[D][1],B,"y")}else{if(this.fillToZero){n=this._yaxis.series_u2p(0)}else{n=z.canvas.height}}C.push([q-this.barWidth/2,n]);C.push([q-this.barWidth/2,G[D][1]]);C.push([q+this.barWidth/2,G[D][1]]);C.push([q+this.barWidth/2,n]);this.renderer.shadowRenderer.draw(z,C,w)}}else{if(this.barDirection=="horizontal"){for(var D=0;D<G.length;D++){if(this.data[D][0]==null){continue}C=[];var q=G[D][1]-this._barNudge;var J;if(this._stack&&this._prevGridData.length){J=i(this.index,D,this._plotData[D][0],B,"x")}else{if(this.fillToZero){J=this._xaxis.series_u2p(0)}else{J=0}}C.push([J,q+this.barWidth/2]);C.push([G[D][0],q+this.barWidth/2]);C.push([G[D][0],q-this.barWidth/2]);C.push([J,q-this.barWidth/2]);this.renderer.shadowRenderer.draw(z,C,w)}}}}}};function h(q,p,n){for(var o=0;o<this.series.length;o++){if(this.series[o].renderer.constructor==d.jqplot.BarRenderer){if(this.series[o].highlightMouseOver){this.series[o].highlightMouseDown=false}}}}function j(){if(this.plugins.barRenderer&&this.plugins.barRenderer.highlightCanvas){this.plugins.barRenderer.highlightCanvas.resetCanvas();this.plugins.barRenderer.highlightCanvas=null}this.plugins.barRenderer={highlightedSeriesIndex:null};this.plugins.barRenderer.highlightCanvas=new d.jqplot.GenericCanvas();this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-barRenderer-highlight-canvas",this._plotDimensions,this));this.plugins.barRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(n){k(n.data.plot)})}function c(u,t,q,p){var o=u.series[t];var n=u.plugins.barRenderer.highlightCanvas;n._ctx.clearRect(0,0,n._ctx.canvas.width,n._ctx.canvas.height);o._highlightedPoint=q;u.plugins.barRenderer.highlightedSeriesIndex=t;var r={fillStyle:o.highlightColors[q]};o.renderer.shapeRenderer.draw(n._ctx,p,r);n=null}function k(p){var n=p.plugins.barRenderer.highlightCanvas;n._ctx.clearRect(0,0,n._ctx.canvas.width,n._ctx.canvas.height);for(var o=0;o<p.series.length;o++){p.series[o]._highlightedPoint=null}p.plugins.barRenderer.highlightedSeriesIndex=null;p.target.trigger("jqplotDataUnhighlight");n=null}function b(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];var o=jQuery.Event("jqplotDataMouseOver");o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p);if(s.series[p[0]].show&&s.series[p[0]].highlightMouseOver&&!(p[0]==s.plugins.barRenderer.highlightedSeriesIndex&&p[1]==s.series[p[0]]._highlightedPoint)){var n=jQuery.Event("jqplotDataHighlight");n.which=r.which;n.pageX=r.pageX;n.pageY=r.pageY;s.target.trigger(n,p);c(s,t.seriesIndex,t.pointIndex,t.points)}}else{if(t==null){k(s)}}}function a(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];if(r.series[o[0]].highlightMouseDown&&!(o[0]==r.plugins.barRenderer.highlightedSeriesIndex&&o[1]==r.series[o[0]]._highlightedPoint)){var n=jQuery.Event("jqplotDataHighlight");n.which=q.which;n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o);c(r,s.seriesIndex,s.pointIndex,s.points)}}else{if(s==null){k(r)}}}function l(p,o,s,r,q){var n=q.plugins.barRenderer.highlightedSeriesIndex;if(n!=null&&q.series[n].highlightMouseDown){k(q)}}function e(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];var n=jQuery.Event("jqplotDataClick");n.which=q.which;n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o)}}function m(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];var n=s.plugins.barRenderer.highlightedSeriesIndex;if(n!=null&&s.series[n].highlightMouseDown){k(s)}var o=jQuery.Event("jqplotDataRightClick");o.which=r.which;o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p)}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.blockRenderer.js b/web/extensions/jqplot/plugins/jqplot.blockRenderer.js new file mode 100644 index 0000000..e7c782c --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.blockRenderer.js @@ -0,0 +1,235 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.BlockRenderer + * Plugin renderer to draw a x-y block chart. A Block chart has data points displayed as + * colored squares with a text label inside. Data must be supplied in the form: + * + * > [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...] + * + * The label and css object are optional. If the label is ommitted, the + * box will collapse unless a css height and/or width is specified. + * + * The css object is an object specifying css properties + * such as: + * + * > {background:'#4f98a5', border:'3px solid gray', padding:'1px'} + * + * Note that css properties specified with the data point override defaults + * specified with the series. + * + */ + $.jqplot.BlockRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer; + + // called with scope of a series + $.jqplot.BlockRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: css + // default css styles that will be applied to all data blocks. + // these values will be overridden by css styles supplied with the + // individulal data points. + this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'}; + // prop: escapeHtml + // true to escape html in the box label. + this.escapeHtml = false; + // prop: insertBreaks + // true to turn spaces in data block label into html breaks <br />. + this.insertBreaks = true; + // prop: varyBlockColors + // true to vary the color of each block in this series according to + // the seriesColors array. False to set each block to the color + // specified on this series. This has no effect if a css background color + // option is specified in the renderer css options. + this.varyBlockColors = false; + $.extend(true, this, options); + if (this.css.backgroundColor) { + this.color = this.css.backgroundColor; + } + else if (this.css.background) { + this.color = this.css.background; + } + else if (!this.varyBlockColors) { + this.css.background = this.color; + } + this.canvas = new $.jqplot.BlockCanvas(); + this.shadowCanvas = new $.jqplot.BlockCanvas(); + this.canvas._plotDimensions = this._plotDimensions; + this.shadowCanvas._plotDimensions = this._plotDimensions; + this._type = 'block'; + + // group: Methods + // + // Method: moveBlock + // Moves an individual block. More efficient than redrawing + // the whole series by calling plot.drawSeries(). + // Properties: + // idx - the 0 based index of the block or point in this series. + // x - the x coordinate in data units (value on x axis) to move the block to. + // y - the y coordinate in data units (value on the y axis) to move the block to. + // duration - optional parameter to create an animated movement. Can be a + // number (higher is slower animation) or 'fast', 'normal' or 'slow'. If not + // provided, the element is moved without any animation. + this.moveBlock = function (idx, x, y, duration) { + // update plotData, stackData, data and gridData + // x and y are in data coordinates. + var el = this.canvas._elem.children(':eq('+idx+')'); + this.data[idx][0] = x; + this.data[idx][1] = y; + this._plotData[idx][0] = x; + this._plotData[idx][1] = y; + this._stackData[idx][0] = x; + this._stackData[idx][1] = y; + this.gridData[idx][0] = this._xaxis.series_u2p(x); + this.gridData[idx][1] = this._yaxis.series_u2p(y); + var w = el.outerWidth(); + var h = el.outerHeight(); + var left = this.gridData[idx][0] - w/2 + 'px'; + var top = this.gridData[idx][1] - h/2 + 'px'; + if (duration) { + if (parseInt(duration, 10)) { + duration = parseInt(duration, 10); + } + el.animate({left:left, top:top}, duration); + } + else { + el.css({left:left, top:top}); + } + el = null; + }; + }; + + // called with scope of series + $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) { + if (this.plugins.pointLabels) { + this.plugins.pointLabels.show = false; + } + var i, el, d, gd, t, css, w, h, left, top; + var opts = (options != undefined) ? options : {}; + var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); + this.canvas._elem.empty(); + for (i=0; i<this.gridData.length; i++) { + d = this.data[i]; + gd = this.gridData[i]; + t = ''; + css = {}; + if (typeof d[2] == 'string') { + t = d[2]; + } + else if (typeof d[2] == 'object') { + css = d[2]; + } + if (typeof d[3] == 'object') { + css = d[3]; + } + if (this.insertBreaks){ + t = t.replace(/ /g, '<br />'); + } + css = $.extend(true, {}, this.css, css); + // create a div + el = $('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>'); + this.canvas._elem.append(el); + // set text + this.escapeHtml ? el.text(t) : el.html(t); + // style it + // remove styles we don't want overridden. + delete css.position; + delete css.marginRight; + delete css.marginLeft; + if (!css.background && !css.backgroundColor && !css.backgroundImage){ + css.background = colorGenerator.next(); + } + el.css(css); + w = el.outerWidth(); + h = el.outerHeight(); + left = gd[0] - w/2 + 'px'; + top = gd[1] - h/2 + 'px'; + el.css({left:left, top:top}); + el = null; + } + }; + + $.jqplot.BlockCanvas = function() { + $.jqplot.ElemContainer.call(this); + this._ctx; + }; + + $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer(); + $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas; + + $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { + this._offsets = offsets; + var klass = 'jqplot-blockCanvas'; + if (clss != undefined) { + klass = clss; + } + var elem; + // if this canvas already has a dom element, don't make a new one. + if (this._elem) { + elem = this._elem.get(0); + } + else { + elem = document.createElement('div'); + } + // if new plotDimensions supplied, use them. + if (plotDimensions != undefined) { + this._plotDimensions = plotDimensions; + } + + var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; + var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; + this._elem = $(elem); + this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); + + this._elem.addClass(klass); + return this._elem; + }; + + $.jqplot.BlockCanvas.prototype.setContext = function() { + this._ctx = { + canvas:{ + width:0, + height:0 + }, + clearRect:function(){return null;} + }; + return this._ctx; + }; + +})(jQuery); + + \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.blockRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.blockRenderer.min.js new file mode 100644 index 0000000..d497d6d --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.blockRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.BlockRenderer=function(){a.jqplot.LineRenderer.call(this)};a.jqplot.BlockRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.BlockRenderer.prototype.constructor=a.jqplot.BlockRenderer;a.jqplot.BlockRenderer.prototype.init=function(b){this.css={padding:"2px",border:"1px solid #999",textAlign:"center"};this.escapeHtml=false;this.insertBreaks=true;this.varyBlockColors=false;a.extend(true,this,b);if(this.css.backgroundColor){this.color=this.css.backgroundColor}else{if(this.css.background){this.color=this.css.background}else{if(!this.varyBlockColors){this.css.background=this.color}}}this.canvas=new a.jqplot.BlockCanvas();this.shadowCanvas=new a.jqplot.BlockCanvas();this.canvas._plotDimensions=this._plotDimensions;this.shadowCanvas._plotDimensions=this._plotDimensions;this._type="block";this.moveBlock=function(l,j,i,e){var c=this.canvas._elem.children(":eq("+l+")");this.data[l][0]=j;this.data[l][1]=i;this._plotData[l][0]=j;this._plotData[l][1]=i;this._stackData[l][0]=j;this._stackData[l][1]=i;this.gridData[l][0]=this._xaxis.series_u2p(j);this.gridData[l][1]=this._yaxis.series_u2p(i);var k=c.outerWidth();var f=c.outerHeight();var d=this.gridData[l][0]-k/2+"px";var g=this.gridData[l][1]-f/2+"px";if(e){if(parseInt(e,10)){e=parseInt(e,10)}c.animate({left:d,top:g},e)}else{c.css({left:d,top:g})}c=null}};a.jqplot.BlockRenderer.prototype.draw=function(q,o,r){if(this.plugins.pointLabels){this.plugins.pointLabels.show=false}var f,c,l,o,p,k,n,g,e,m;var b=(r!=undefined)?r:{};var j=new a.jqplot.ColorGenerator(this.seriesColors);this.canvas._elem.empty();for(f=0;f<this.gridData.length;f++){l=this.data[f];o=this.gridData[f];p="";k={};if(typeof l[2]=="string"){p=l[2]}else{if(typeof l[2]=="object"){k=l[2]}}if(typeof l[3]=="object"){k=l[3]}if(this.insertBreaks){p=p.replace(/ /g,"<br />")}k=a.extend(true,{},this.css,k);c=a('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>');this.canvas._elem.append(c);this.escapeHtml?c.text(p):c.html(p);delete k.position;delete k.marginRight;delete k.marginLeft;if(!k.background&&!k.backgroundColor&&!k.backgroundImage){k.background=j.next()}c.css(k);n=c.outerWidth();g=c.outerHeight();e=o[0]-n/2+"px";m=o[1]-g/2+"px";c.css({left:e,top:m});c=null}};a.jqplot.BlockCanvas=function(){a.jqplot.ElemContainer.call(this);this._ctx};a.jqplot.BlockCanvas.prototype=new a.jqplot.ElemContainer();a.jqplot.BlockCanvas.prototype.constructor=a.jqplot.BlockCanvas;a.jqplot.BlockCanvas.prototype.createElement=function(i,e,c){this._offsets=i;var b="jqplot-blockCanvas";if(e!=undefined){b=e}var g;if(this._elem){g=this._elem.get(0)}else{g=document.createElement("div")}if(c!=undefined){this._plotDimensions=c}var d=this._plotDimensions.width-this._offsets.left-this._offsets.right+"px";var f=this._plotDimensions.height-this._offsets.top-this._offsets.bottom+"px";this._elem=a(g);this._elem.css({position:"absolute",width:d,height:f,left:this._offsets.left,top:this._offsets.top});this._elem.addClass(b);return this._elem};a.jqplot.BlockCanvas.prototype.setContext=function(){this._ctx={canvas:{width:0,height:0},clearRect:function(){return null}};return this._ctx}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.js b/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.js new file mode 100644 index 0000000..1629d85 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.js @@ -0,0 +1,759 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + var arrayMax = function( array ){ + return Math.max.apply( Math, array ); + }; + var arrayMin = function( array ){ + return Math.min.apply( Math, array ); + }; + + /** + * Class: $.jqplot.BubbleRenderer + * Plugin renderer to draw a bubble chart. A Bubble chart has data points displayed as + * colored circles with an optional text label inside. To use + * the bubble renderer, you must include the bubble renderer like: + * + * > <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.bubbleRenderer.js"></script> + * + * Data must be supplied in + * the form: + * + * > [[x1, y1, r1, <label or {label:'text', color:color}>], ...] + * + * where the label or options + * object is optional. + * + * Note that all bubble colors will be the same + * unless the "varyBubbleColors" option is set to true. Colors can be specified in the data array + * or in the seriesColors array option on the series. If no colors are defined, the default jqPlot + * series of 16 colors are used. Colors are automatically cycled around again if there are more + * bubbles than colors. + * + * Bubbles are autoscaled by default to fit within the chart area while maintaining + * relative sizes. If the "autoscaleBubbles" option is set to false, the r(adius) values + * in the data array a treated as literal pixel values for the radii of the bubbles. + * + * Properties are passed into the bubble renderer in the rendererOptions object of + * the series options like: + * + * > seriesDefaults: { + * > renderer: $.jqplot.BubbleRenderer, + * > rendererOptions: { + * > bubbleAlpha: 0.7, + * > varyBubbleColors: false + * > } + * > } + * + */ + $.jqplot.BubbleRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.BubbleRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.BubbleRenderer.prototype.constructor = $.jqplot.BubbleRenderer; + + // called with scope of a series + $.jqplot.BubbleRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + // prop: varyBubbleColors + // True to vary the color of each bubble in this series according to + // the seriesColors array. False to set each bubble to the color + // specified on this series. This has no effect if a css background color + // option is specified in the renderer css options. + this.varyBubbleColors = true; + // prop: autoscaleBubbles + // True to scale the bubble radius based on plot size. + // False will use the radius value as provided as a raw pixel value for + // bubble radius. + this.autoscaleBubbles = true; + // prop: autoscaleMultiplier + // Multiplier the bubble size if autoscaleBubbles is true. + this.autoscaleMultiplier = 1.0; + // prop: autoscalePointsFactor + // Factor which decreases bubble size based on how many bubbles on on the chart. + // 0 means no adjustment for number of bubbles. Negative values will decrease + // size of bubbles as more bubbles are added. Values between 0 and -0.2 + // should work well. + this.autoscalePointsFactor = -0.07; + // prop: escapeHtml + // True to escape html in bubble label text. + this.escapeHtml = true; + // prop: highlightMouseOver + // True to highlight bubbles when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a slice. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a bubble. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // An array of colors to use when highlighting a slice. Calculated automatically + // if not supplied. + this.highlightColors = []; + // prop: bubbleAlpha + // Alpha transparency to apply to all bubbles in this series. + this.bubbleAlpha = 1.0; + // prop: highlightAlpha + // Alpha transparency to apply when highlighting bubble. + // Set to value of bubbleAlpha by default. + this.highlightAlpha = null; + // prop: bubbleGradients + // True to color the bubbles with gradient fills instead of flat colors. + // NOT AVAILABLE IN IE due to lack of excanvas support for radial gradient fills. + // will be ignored in IE. + this.bubbleGradients = false; + // prop: showLabels + // True to show labels on bubbles (if any), false to not show. + this.showLabels = true; + // array of [point index, radius] which will be sorted in descending order to plot + // largest points below smaller points. + this.radii = []; + this.maxRadius = 0; + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + // array of jQuery labels. + this.labels = []; + this.bubbleCanvases = []; + this._type = 'bubble'; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + $.extend(true, this, options); + + if (this.highlightAlpha == null) { + this.highlightAlpha = this.bubbleAlpha; + if (this.bubbleGradients) { + this.highlightAlpha = 0.35; + } + } + + this.autoscaleMultiplier = this.autoscaleMultiplier * Math.pow(this.data.length, this.autoscalePointsFactor); + + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + + // adjust the series colors for options colors passed in with data or for alpha. + // note, this can leave undefined holes in the seriesColors array. + var comps; + for (var i=0; i<this.data.length; i++) { + var color = null; + var d = this.data[i]; + this.maxRadius = Math.max(this.maxRadius, d[2]); + if (d[3]) { + if (typeof(d[3]) == 'object') { + color = d[3]['color']; + } + } + + if (color == null) { + if (this.seriesColors[i] != null) { + color = this.seriesColors[i]; + } + } + + if (color && this.bubbleAlpha < 1.0) { + comps = $.jqplot.getColorComponents(color); + color = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', '+this.bubbleAlpha+')'; + } + + if (color) { + this.seriesColors[i] = color; + } + } + + if (!this.varyBubbleColors) { + this.seriesColors = [this.color]; + } + + this.colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); + + // set highlight colors if none provided + if (this.highlightColors.length == 0) { + for (var i=0; i<this.seriesColors.length; i++){ + var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + this.highlightColors.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+', '+this.highlightAlpha+')'); + } + } + + this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); + + var sopts = {fill:true, isarc:true, angle:this.shadowAngle, alpha:this.shadowAlpha, closePath:true}; + + this.renderer.shadowRenderer.init(sopts); + + this.canvas = new $.jqplot.DivCanvas(); + this.canvas._plotDimensions = this._plotDimensions; + + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + plot.postDrawHooks.addOnce(postPlotDraw); + + }; + + + // converts the user data values to grid coordinates and stores them + // in the gridData array. + // Called with scope of a series. + $.jqplot.BubbleRenderer.prototype.setGridData = function(plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var data = this._plotData; + this.gridData = []; + var radii = []; + this.radii = []; + var dim = Math.min(plot._height, plot._width); + for (var i=0; i<this.data.length; i++) { + if (data[i] != null) { + this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); + this.radii.push([i, data[i][2]]); + radii.push(data[i][2]); + } + } + var r, val, maxr = this.maxRadius = arrayMax(radii); + var l = this.gridData.length; + if (this.autoscaleBubbles) { + for (var i=0; i<l; i++) { + val = radii[i]/maxr; + r = this.autoscaleMultiplier * dim / 6; + this.gridData[i][2] = r * val; + } + } + + this.radii.sort(function(a, b) { return b[1] - a[1]; }); + }; + + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.BubbleRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var gd = []; + var radii = []; + this.radii = []; + var dim = Math.min(plot._height, plot._width); + for (var i=0; i<data.length; i++) { + if (data[i] != null) { + gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); + radii.push(data[i][2]); + this.radii.push([i, data[i][2]]); + } + } + var r, val, maxr = this.maxRadius = arrayMax(radii); + var l = this.gridData.length; + if (this.autoscaleBubbles) { + for (var i=0; i<l; i++) { + val = radii[i]/maxr; + r = this.autoscaleMultiplier * dim / 6; + gd[i][2] = r * val; + } + } + this.radii.sort(function(a, b) { return b[1] - a[1]; }); + return gd; + }; + + // called with scope of series + $.jqplot.BubbleRenderer.prototype.draw = function (ctx, gd, options) { + if (this.plugins.pointLabels) { + this.plugins.pointLabels.show = false; + } + var opts = (options != undefined) ? options : {}; + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + this.canvas._elem.empty(); + for (var i=0; i<this.radii.length; i++) { + var idx = this.radii[i][0]; + var t=null; + var color = null; + var el = null; + var tel = null; + var d = this.data[idx]; + var gd = this.gridData[idx]; + if (d[3]) { + if (typeof(d[3]) == 'object') { + t = d[3]['label']; + } + else if (typeof(d[3]) == 'string') { + t = d[3]; + } + } + + // color = (this.varyBubbleColors) ? this.colorGenerator.get(idx) : this.color; + color = this.colorGenerator.get(idx); + + // If we're drawing a shadow, expand the canvas dimensions to accomodate. + var canvasRadius = gd[2]; + var offset, depth; + if (this.shadow) { + offset = (0.7 + gd[2]/40).toFixed(1); + depth = 1 + Math.ceil(gd[2]/15); + canvasRadius += offset*depth; + } + this.bubbleCanvases[idx] = new $.jqplot.BubbleCanvas(); + this.canvas._elem.append(this.bubbleCanvases[idx].createElement(gd[0], gd[1], canvasRadius)); + this.bubbleCanvases[idx].setContext(); + var ctx = this.bubbleCanvases[idx]._ctx; + var x = ctx.canvas.width/2; + var y = ctx.canvas.height/2; + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [x, y, gd[2], 0, 2*Math.PI], {offset: offset, depth: depth}); + } + this.bubbleCanvases[idx].draw(gd[2], color, this.bubbleGradients, this.shadowAngle/180*Math.PI); + + // now draw label. + if (t && this.showLabels) { + tel = $('<div style="position:absolute;" class="jqplot-bubble-label"></div>'); + if (this.escapeHtml) { + tel.text(t); + } + else { + tel.html(t); + } + this.canvas._elem.append(tel); + var h = $(tel).outerHeight(); + var w = $(tel).outerWidth(); + var top = gd[1] - 0.5*h; + var left = gd[0] - 0.5*w; + tel.css({top: top, left: left}); + this.labels[idx] = $(tel); + } + } + }; + + + $.jqplot.DivCanvas = function() { + $.jqplot.ElemContainer.call(this); + this._ctx; + }; + + $.jqplot.DivCanvas.prototype = new $.jqplot.ElemContainer(); + $.jqplot.DivCanvas.prototype.constructor = $.jqplot.DivCanvas; + + $.jqplot.DivCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { + this._offsets = offsets; + var klass = 'jqplot-DivCanvas'; + if (clss != undefined) { + klass = clss; + } + var elem; + // if this canvas already has a dom element, don't make a new one. + if (this._elem) { + elem = this._elem.get(0); + } + else { + elem = document.createElement('div'); + } + // if new plotDimensions supplied, use them. + if (plotDimensions != undefined) { + this._plotDimensions = plotDimensions; + } + + var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; + var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; + this._elem = $(elem); + this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); + + this._elem.addClass(klass); + return this._elem; + }; + + $.jqplot.DivCanvas.prototype.setContext = function() { + this._ctx = { + canvas:{ + width:0, + height:0 + }, + clearRect:function(){return null;} + }; + return this._ctx; + }; + + $.jqplot.BubbleCanvas = function() { + $.jqplot.ElemContainer.call(this); + this._ctx; + }; + + $.jqplot.BubbleCanvas.prototype = new $.jqplot.ElemContainer(); + $.jqplot.BubbleCanvas.prototype.constructor = $.jqplot.BubbleCanvas; + + // initialize with the x,y pont of bubble center and the bubble radius. + $.jqplot.BubbleCanvas.prototype.createElement = function(x, y, r) { + var klass = 'jqplot-bubble-point'; + + var elem; + // if this canvas already has a dom element, don't make a new one. + if (this._elem) { + elem = this._elem.get(0); + } + else { + elem = document.createElement('canvas'); + } + + elem.width = (r != null) ? 2*r : elem.width; + elem.height = (r != null) ? 2*r : elem.height; + this._elem = $(elem); + var l = (x != null && r != null) ? x - r : this._elem.css('left'); + var t = (y != null && r != null) ? y - r : this._elem.css('top'); + this._elem.css({ position: 'absolute', left: l, top: t }); + + this._elem.addClass(klass); + if ($.jqplot.use_excanvas) { + window.G_vmlCanvasManager.init_(document); + elem = window.G_vmlCanvasManager.initElement(elem); + } + + return this._elem; + }; + + $.jqplot.BubbleCanvas.prototype.draw = function(r, color, gradients, angle) { + var ctx = this._ctx; + // r = Math.floor(r*1.04); + // var x = Math.round(ctx.canvas.width/2); + // var y = Math.round(ctx.canvas.height/2); + var x = ctx.canvas.width/2; + var y = ctx.canvas.height/2; + ctx.save(); + if (gradients && !$.jqplot.use_excanvas) { + r = r*1.04; + var comps = $.jqplot.getColorComponents(color); + var colorinner = 'rgba('+Math.round(comps[0]+0.8*(255-comps[0]))+', '+Math.round(comps[1]+0.8*(255-comps[1]))+', '+Math.round(comps[2]+0.8*(255-comps[2]))+', '+comps[3]+')'; + var colorend = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', 0)'; + // var rinner = Math.round(0.35 * r); + // var xinner = Math.round(x - Math.cos(angle) * 0.33 * r); + // var yinner = Math.round(y - Math.sin(angle) * 0.33 * r); + var rinner = 0.35 * r; + var xinner = x - Math.cos(angle) * 0.33 * r; + var yinner = y - Math.sin(angle) * 0.33 * r; + var radgrad = ctx.createRadialGradient(xinner, yinner, rinner, x, y, r); + radgrad.addColorStop(0, colorinner); + radgrad.addColorStop(0.93, color); + radgrad.addColorStop(0.96, colorend); + radgrad.addColorStop(1, colorend); + // radgrad.addColorStop(.98, colorend); + ctx.fillStyle = radgrad; + ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height); + } + else { + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.beginPath(); + var ang = 2*Math.PI; + ctx.arc(x, y, r, 0, ang, 0); + ctx.closePath(); + ctx.fill(); + } + ctx.restore(); + }; + + $.jqplot.BubbleCanvas.prototype.setContext = function() { + this._ctx = this._elem.get(0).getContext("2d"); + return this._ctx; + }; + + $.jqplot.BubbleAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.BubbleAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.BubbleAxisRenderer.prototype.constructor = $.jqplot.BubbleAxisRenderer; + + // called with scope of axis object. + $.jqplot.BubbleAxisRenderer.prototype.init = function(options){ + $.extend(true, this, options); + var db = this._dataBounds; + var minsidx = 0, + minpidx = 0, + maxsidx = 0, + maxpidx = 0, + maxr = 0, + minr = 0, + minMaxRadius = 0, + maxMaxRadius = 0, + maxMult = 0, + minMult = 0; + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + var d = s._plotData; + + for (var j=0; j<d.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + if (d[j][0] < db.min || db.min == null) { + db.min = d[j][0]; + minsidx=i; + minpidx=j; + minr = d[j][2]; + minMaxRadius = s.maxRadius; + minMult = s.autoscaleMultiplier; + } + if (d[j][0] > db.max || db.max == null) { + db.max = d[j][0]; + maxsidx=i; + maxpidx=j; + maxr = d[j][2]; + maxMaxRadius = s.maxRadius; + maxMult = s.autoscaleMultiplier; + } + } + else { + if (d[j][1] < db.min || db.min == null) { + db.min = d[j][1]; + minsidx=i; + minpidx=j; + minr = d[j][2]; + minMaxRadius = s.maxRadius; + minMult = s.autoscaleMultiplier; + } + if (d[j][1] > db.max || db.max == null) { + db.max = d[j][1]; + maxsidx=i; + maxpidx=j; + maxr = d[j][2]; + maxMaxRadius = s.maxRadius; + maxMult = s.autoscaleMultiplier; + } + } + } + } + + var minRatio = minr/minMaxRadius; + var maxRatio = maxr/maxMaxRadius; + + // need to estimate the effect of the radius on total axis span and adjust axis accordingly. + var span = db.max - db.min; + // var dim = (this.name == 'xaxis' || this.name == 'x2axis') ? this._plotDimensions.width : this._plotDimensions.height; + var dim = Math.min(this._plotDimensions.width, this._plotDimensions.height); + + var minfact = minRatio * minMult/3 * span; + var maxfact = maxRatio * maxMult/3 * span; + db.max += maxfact; + db.min -= minfact; + }; + + function highlight (plot, sidx, pidx) { + plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); + var s = plot.series[sidx]; + var canvas = plot.plugins.bubbleRenderer.highlightCanvas; + var ctx = canvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.bubbleRenderer.highlightedSeriesIndex = sidx; + + var color = s.highlightColorGenerator.get(pidx); + var x = s.gridData[pidx][0], + y = s.gridData[pidx][1], + r = s.gridData[pidx][2]; + ctx.save(); + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.arc(x, y, r, 0, 2*Math.PI, 0); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + // bring label to front + if (s.labels[pidx]) { + plot.plugins.bubbleRenderer.highlightLabel = s.labels[pidx].clone(); + plot.plugins.bubbleRenderer.highlightLabel.appendTo(plot.plugins.bubbleRenderer.highlightLabelCanvas); + plot.plugins.bubbleRenderer.highlightLabel.addClass('jqplot-bubble-label-highlight'); + } + } + + function unhighlight (plot) { + var canvas = plot.plugins.bubbleRenderer.highlightCanvas; + var sidx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; + plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.bubbleRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + } + + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var si = neighbor.seriesIndex; + var pi = neighbor.pointIndex; + var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var si = neighbor.seriesIndex; + var pi = neighbor.pointIndex; + var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var si = neighbor.seriesIndex; + var pi = neighbor.pointIndex; + var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var si = neighbor.seriesIndex; + var pi = neighbor.pointIndex; + var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; + var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.bubbleRenderer && this.plugins.bubbleRenderer.highlightCanvas) { + this.plugins.bubbleRenderer.highlightCanvas.resetCanvas(); + this.plugins.bubbleRenderer.highlightCanvas = null; + } + + this.plugins.bubbleRenderer = {highlightedSeriesIndex:null}; + this.plugins.bubbleRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + this.plugins.bubbleRenderer.highlightLabel = null; + this.plugins.bubbleRenderer.highlightLabelCanvas = $('<div style="position:absolute;"></div>'); + var top = this._gridPadding.top; + var left = this._gridPadding.left; + var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right; + var height = this._plotDimensions.height - this._gridPadding.top - this._gridPadding.bottom; + this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:top, left:left, width:width+'px', height:height+'px'}); + + this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-bubbleRenderer-highlight-canvas', this._plotDimensions, this)); + this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas); + + var hctx = this.plugins.bubbleRenderer.highlightCanvas.setContext(); + } + + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.seriesDefaults = options.seriesDefaults || {}; + // only set these if there is a Bubble series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.BubbleRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.BubbleRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.BubbleAxisRenderer; + options.sortData = false; + } + } + + $.jqplot.preInitHooks.push(preInit); + +})(jQuery); + + \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.min.js new file mode 100644 index 0000000..dc0c1ef --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.bubbleRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(f){var d=function(m){return Math.max.apply(Math,m)};var j=function(m){return Math.min.apply(Math,m)};f.jqplot.BubbleRenderer=function(){f.jqplot.LineRenderer.call(this)};f.jqplot.BubbleRenderer.prototype=new f.jqplot.LineRenderer();f.jqplot.BubbleRenderer.prototype.constructor=f.jqplot.BubbleRenderer;f.jqplot.BubbleRenderer.prototype.init=function(w,t){this.varyBubbleColors=true;this.autoscaleBubbles=true;this.autoscaleMultiplier=1;this.autoscalePointsFactor=-0.07;this.escapeHtml=true;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.bubbleAlpha=1;this.highlightAlpha=null;this.bubbleGradients=false;this.showLabels=true;this.radii=[];this.maxRadius=0;this._highlightedPoint=null;this.labels=[];this.bubbleCanvases=[];this._type="bubble";if(w.highlightMouseDown&&w.highlightMouseOver==null){w.highlightMouseOver=false}f.extend(true,this,w);if(this.highlightAlpha==null){this.highlightAlpha=this.bubbleAlpha;if(this.bubbleGradients){this.highlightAlpha=0.35}}this.autoscaleMultiplier=this.autoscaleMultiplier*Math.pow(this.data.length,this.autoscalePointsFactor);this._highlightedPoint=null;var n;for(var r=0;r<this.data.length;r++){var p=null;var v=this.data[r];this.maxRadius=Math.max(this.maxRadius,v[2]);if(v[3]){if(typeof(v[3])=="object"){p=v[3]["color"]}}if(p==null){if(this.seriesColors[r]!=null){p=this.seriesColors[r]}}if(p&&this.bubbleAlpha<1){n=f.jqplot.getColorComponents(p);p="rgba("+n[0]+", "+n[1]+", "+n[2]+", "+this.bubbleAlpha+")"}if(p){this.seriesColors[r]=p}}if(!this.varyBubbleColors){this.seriesColors=[this.color]}this.colorGenerator=new f.jqplot.ColorGenerator(this.seriesColors);if(this.highlightColors.length==0){for(var r=0;r<this.seriesColors.length;r++){var o=f.jqplot.getColorComponents(this.seriesColors[r]);var u=[o[0],o[1],o[2]];var s=u[0]+u[1]+u[2];for(var q=0;q<3;q++){u[q]=(s>570)?u[q]*0.8:u[q]+0.3*(255-u[q]);u[q]=parseInt(u[q],10)}this.highlightColors.push("rgba("+u[0]+","+u[1]+","+u[2]+", "+this.highlightAlpha+")")}}this.highlightColorGenerator=new f.jqplot.ColorGenerator(this.highlightColors);var m={fill:true,isarc:true,angle:this.shadowAngle,alpha:this.shadowAlpha,closePath:true};this.renderer.shadowRenderer.init(m);this.canvas=new f.jqplot.DivCanvas();this.canvas._plotDimensions=this._plotDimensions;t.eventListenerHooks.addOnce("jqplotMouseMove",a);t.eventListenerHooks.addOnce("jqplotMouseDown",b);t.eventListenerHooks.addOnce("jqplotMouseUp",k);t.eventListenerHooks.addOnce("jqplotClick",g);t.eventListenerHooks.addOnce("jqplotRightClick",l);t.postDrawHooks.addOnce(h)};f.jqplot.BubbleRenderer.prototype.setGridData=function(w){var q=this._xaxis.series_u2p;var m=this._yaxis.series_u2p;var t=this._plotData;this.gridData=[];var s=[];this.radii=[];var v=Math.min(w._height,w._width);for(var u=0;u<this.data.length;u++){if(t[u]!=null){this.gridData.push([q.call(this._xaxis,t[u][0]),m.call(this._yaxis,t[u][1]),t[u][2]]);this.radii.push([u,t[u][2]]);s.push(t[u][2])}}var n,o,x=this.maxRadius=d(s);var p=this.gridData.length;if(this.autoscaleBubbles){for(var u=0;u<p;u++){o=s[u]/x;n=this.autoscaleMultiplier*v/6;this.gridData[u][2]=n*o}}this.radii.sort(function(y,r){return r[1]-y[1]})};f.jqplot.BubbleRenderer.prototype.makeGridData=function(t,w){var q=this._xaxis.series_u2p;var n=this._yaxis.series_u2p;var x=[];var s=[];this.radii=[];var v=Math.min(w._height,w._width);for(var u=0;u<t.length;u++){if(t[u]!=null){x.push([q.call(this._xaxis,t[u][0]),n.call(this._yaxis,t[u][1]),t[u][2]]);s.push(t[u][2]);this.radii.push([u,t[u][2]])}}var m,o,y=this.maxRadius=d(s);var p=this.gridData.length;if(this.autoscaleBubbles){for(var u=0;u<p;u++){o=s[u]/y;m=this.autoscaleMultiplier*v/6;x[u][2]=m*o}}this.radii.sort(function(z,r){return r[1]-z[1]});return x};f.jqplot.BubbleRenderer.prototype.draw=function(D,J,n){if(this.plugins.pointLabels){this.plugins.pointLabels.show=false}var A=(n!=undefined)?n:{};var r=(A.shadow!=undefined)?A.shadow:this.shadow;this.canvas._elem.empty();for(var G=0;G<this.radii.length;G++){var C=this.radii[G][0];var z=null;var F=null;var m=null;var p=null;var I=this.data[C];var J=this.gridData[C];if(I[3]){if(typeof(I[3])=="object"){z=I[3]["label"]}else{if(typeof(I[3])=="string"){z=I[3]}}}F=this.colorGenerator.get(C);var E=J[2];var q,K;if(this.shadow){q=(0.7+J[2]/40).toFixed(1);K=1+Math.ceil(J[2]/15);E+=q*K}this.bubbleCanvases[C]=new f.jqplot.BubbleCanvas();this.canvas._elem.append(this.bubbleCanvases[C].createElement(J[0],J[1],E));this.bubbleCanvases[C].setContext();var D=this.bubbleCanvases[C]._ctx;var u=D.canvas.width/2;var s=D.canvas.height/2;if(this.shadow){this.renderer.shadowRenderer.draw(D,[u,s,J[2],0,2*Math.PI],{offset:q,depth:K})}this.bubbleCanvases[C].draw(J[2],F,this.bubbleGradients,this.shadowAngle/180*Math.PI);if(z&&this.showLabels){p=f('<div style="position:absolute;" class="jqplot-bubble-label"></div>');if(this.escapeHtml){p.text(z)}else{p.html(z)}this.canvas._elem.append(p);var H=f(p).outerHeight();var v=f(p).outerWidth();var B=J[1]-0.5*H;var o=J[0]-0.5*v;p.css({top:B,left:o});this.labels[C]=f(p)}}};f.jqplot.DivCanvas=function(){f.jqplot.ElemContainer.call(this);this._ctx};f.jqplot.DivCanvas.prototype=new f.jqplot.ElemContainer();f.jqplot.DivCanvas.prototype.constructor=f.jqplot.DivCanvas;f.jqplot.DivCanvas.prototype.createElement=function(s,p,n){this._offsets=s;var m="jqplot-DivCanvas";if(p!=undefined){m=p}var r;if(this._elem){r=this._elem.get(0)}else{r=document.createElement("div")}if(n!=undefined){this._plotDimensions=n}var o=this._plotDimensions.width-this._offsets.left-this._offsets.right+"px";var q=this._plotDimensions.height-this._offsets.top-this._offsets.bottom+"px";this._elem=f(r);this._elem.css({position:"absolute",width:o,height:q,left:this._offsets.left,top:this._offsets.top});this._elem.addClass(m);return this._elem};f.jqplot.DivCanvas.prototype.setContext=function(){this._ctx={canvas:{width:0,height:0},clearRect:function(){return null}};return this._ctx};f.jqplot.BubbleCanvas=function(){f.jqplot.ElemContainer.call(this);this._ctx};f.jqplot.BubbleCanvas.prototype=new f.jqplot.ElemContainer();f.jqplot.BubbleCanvas.prototype.constructor=f.jqplot.BubbleCanvas;f.jqplot.BubbleCanvas.prototype.createElement=function(n,u,s){var m="jqplot-bubble-point";var q;if(this._elem){q=this._elem.get(0)}else{q=document.createElement("canvas")}q.width=(s!=null)?2*s:q.width;q.height=(s!=null)?2*s:q.height;this._elem=f(q);var o=(n!=null&&s!=null)?n-s:this._elem.css("left");var p=(u!=null&&s!=null)?u-s:this._elem.css("top");this._elem.css({position:"absolute",left:o,top:p});this._elem.addClass(m);if(f.jqplot.use_excanvas){window.G_vmlCanvasManager.init_(document);q=window.G_vmlCanvasManager.initElement(q)}return this._elem};f.jqplot.BubbleCanvas.prototype.draw=function(m,s,v,p){var D=this._ctx;var B=D.canvas.width/2;var z=D.canvas.height/2;D.save();if(v&&!f.jqplot.use_excanvas){m=m*1.04;var o=f.jqplot.getColorComponents(s);var u="rgba("+Math.round(o[0]+0.8*(255-o[0]))+", "+Math.round(o[1]+0.8*(255-o[1]))+", "+Math.round(o[2]+0.8*(255-o[2]))+", "+o[3]+")";var t="rgba("+o[0]+", "+o[1]+", "+o[2]+", 0)";var C=0.35*m;var A=B-Math.cos(p)*0.33*m;var n=z-Math.sin(p)*0.33*m;var w=D.createRadialGradient(A,n,C,B,z,m);w.addColorStop(0,u);w.addColorStop(0.93,s);w.addColorStop(0.96,t);w.addColorStop(1,t);D.fillStyle=w;D.fillRect(0,0,D.canvas.width,D.canvas.height)}else{D.fillStyle=s;D.strokeStyle=s;D.lineWidth=1;D.beginPath();var q=2*Math.PI;D.arc(B,z,m,0,q,0);D.closePath();D.fill()}D.restore()};f.jqplot.BubbleCanvas.prototype.setContext=function(){this._ctx=this._elem.get(0).getContext("2d");return this._ctx};f.jqplot.BubbleAxisRenderer=function(){f.jqplot.LinearAxisRenderer.call(this)};f.jqplot.BubbleAxisRenderer.prototype=new f.jqplot.LinearAxisRenderer();f.jqplot.BubbleAxisRenderer.prototype.constructor=f.jqplot.BubbleAxisRenderer;f.jqplot.BubbleAxisRenderer.prototype.init=function(n){f.extend(true,this,n);var I=this._dataBounds;var H=0,v=0,m=0,y=0,q=0,r=0,D=0,t=0,F=0,z=0;for(var E=0;E<this._series.length;E++){var x=this._series[E];var G=x._plotData;for(var B=0;B<G.length;B++){if(this.name=="xaxis"||this.name=="x2axis"){if(G[B][0]<I.min||I.min==null){I.min=G[B][0];H=E;v=B;r=G[B][2];D=x.maxRadius;z=x.autoscaleMultiplier}if(G[B][0]>I.max||I.max==null){I.max=G[B][0];m=E;y=B;q=G[B][2];t=x.maxRadius;F=x.autoscaleMultiplier}}else{if(G[B][1]<I.min||I.min==null){I.min=G[B][1];H=E;v=B;r=G[B][2];D=x.maxRadius;z=x.autoscaleMultiplier}if(G[B][1]>I.max||I.max==null){I.max=G[B][1];m=E;y=B;q=G[B][2];t=x.maxRadius;F=x.autoscaleMultiplier}}}}var o=r/D;var w=q/t;var C=I.max-I.min;var A=Math.min(this._plotDimensions.width,this._plotDimensions.height);var p=o*z/3*C;var u=w*F/3*C;I.max+=u;I.min-=p};function e(p,v,q){p.plugins.bubbleRenderer.highlightLabelCanvas.empty();var z=p.series[v];var n=p.plugins.bubbleRenderer.highlightCanvas;var w=n._ctx;w.clearRect(0,0,w.canvas.width,w.canvas.height);z._highlightedPoint=q;p.plugins.bubbleRenderer.highlightedSeriesIndex=v;var o=z.highlightColorGenerator.get(q);var u=z.gridData[q][0],t=z.gridData[q][1],m=z.gridData[q][2];w.save();w.fillStyle=o;w.strokeStyle=o;w.lineWidth=1;w.beginPath();w.arc(u,t,m,0,2*Math.PI,0);w.closePath();w.fill();w.restore();if(z.labels[q]){p.plugins.bubbleRenderer.highlightLabel=z.labels[q].clone();p.plugins.bubbleRenderer.highlightLabel.appendTo(p.plugins.bubbleRenderer.highlightLabelCanvas);p.plugins.bubbleRenderer.highlightLabel.addClass("jqplot-bubble-label-highlight")}}function i(p){var m=p.plugins.bubbleRenderer.highlightCanvas;var o=p.plugins.bubbleRenderer.highlightedSeriesIndex;p.plugins.bubbleRenderer.highlightLabelCanvas.empty();m._ctx.clearRect(0,0,m._ctx.canvas.width,m._ctx.canvas.height);for(var n=0;n<p.series.length;n++){p.series[n]._highlightedPoint=null}p.plugins.bubbleRenderer.highlightedSeriesIndex=null;p.target.trigger("jqplotDataUnhighlight")}function a(s,p,m,v,r){if(v){var n=v.seriesIndex;var o=v.pointIndex;var q=[n,o,v.data,r.series[n].gridData[o][2]];var t=jQuery.Event("jqplotDataMouseOver");t.pageX=s.pageX;t.pageY=s.pageY;r.target.trigger(t,q);if(r.series[q[0]].highlightMouseOver&&!(q[0]==r.plugins.bubbleRenderer.highlightedSeriesIndex&&q[1]==r.series[q[0]]._highlightedPoint)){var u=jQuery.Event("jqplotDataHighlight");u.which=s.which;u.pageX=s.pageX;u.pageY=s.pageY;r.target.trigger(u,q);e(r,q[0],q[1])}}else{if(v==null){i(r)}}}function b(s,p,m,u,r){if(u){var n=u.seriesIndex;var o=u.pointIndex;var q=[n,o,u.data,r.series[n].gridData[o][2]];if(r.series[q[0]].highlightMouseDown&&!(q[0]==r.plugins.bubbleRenderer.highlightedSeriesIndex&&q[1]==r.series[q[0]]._highlightedPoint)){var t=jQuery.Event("jqplotDataHighlight");t.which=s.which;t.pageX=s.pageX;t.pageY=s.pageY;r.target.trigger(t,q);e(r,q[0],q[1])}}else{if(u==null){i(r)}}}function k(o,n,r,q,p){var m=p.plugins.bubbleRenderer.highlightedSeriesIndex;if(m!=null&&p.series[m].highlightMouseDown){i(p)}}function g(s,p,m,u,r){if(u){var n=u.seriesIndex;var o=u.pointIndex;var q=[n,o,u.data,r.series[n].gridData[o][2]];var t=jQuery.Event("jqplotDataClick");t.which=s.which;t.pageX=s.pageX;t.pageY=s.pageY;r.target.trigger(t,q)}}function l(s,p,m,v,r){if(v){var n=v.seriesIndex;var o=v.pointIndex;var q=[n,o,v.data,r.series[n].gridData[o][2]];var t=r.plugins.bubbleRenderer.highlightedSeriesIndex;if(t!=null&&r.series[t].highlightMouseDown){i(r)}var u=jQuery.Event("jqplotDataRightClick");u.which=s.which;u.pageX=s.pageX;u.pageY=s.pageY;r.target.trigger(u,q)}}function h(){if(this.plugins.bubbleRenderer&&this.plugins.bubbleRenderer.highlightCanvas){this.plugins.bubbleRenderer.highlightCanvas.resetCanvas();this.plugins.bubbleRenderer.highlightCanvas=null}this.plugins.bubbleRenderer={highlightedSeriesIndex:null};this.plugins.bubbleRenderer.highlightCanvas=new f.jqplot.GenericCanvas();this.plugins.bubbleRenderer.highlightLabel=null;this.plugins.bubbleRenderer.highlightLabelCanvas=f('<div style="position:absolute;"></div>');var q=this._gridPadding.top;var p=this._gridPadding.left;var n=this._plotDimensions.width-this._gridPadding.left-this._gridPadding.right;var m=this._plotDimensions.height-this._gridPadding.top-this._gridPadding.bottom;this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:q,left:p,width:n+"px",height:m+"px"});this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-bubbleRenderer-highlight-canvas",this._plotDimensions,this));this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas);var o=this.plugins.bubbleRenderer.highlightCanvas.setContext()}function c(q,p,n){n=n||{};n.axesDefaults=n.axesDefaults||{};n.seriesDefaults=n.seriesDefaults||{};var m=false;if(n.seriesDefaults.renderer==f.jqplot.BubbleRenderer){m=true}else{if(n.series){for(var o=0;o<n.series.length;o++){if(n.series[o].renderer==f.jqplot.BubbleRenderer){m=true}}}}if(m){n.axesDefaults.renderer=f.jqplot.BubbleAxisRenderer;n.sortData=false}}f.jqplot.preInitHooks.push(c)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js b/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js new file mode 100644 index 0000000..6313dc1 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js @@ -0,0 +1,203 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.CanvasAxisLabelRenderer + * Renderer to draw axis labels with a canvas element to support advanced + * featrues such as rotated text. This renderer uses a separate rendering engine + * to draw the text on the canvas. Two modes of rendering the text are available. + * If the browser has native font support for canvas fonts (currently Mozila 3.5 + * and Safari 4), you can enable text rendering with the canvas fillText method. + * You do so by setting the "enableFontSupport" option to true. + * + * Browsers lacking native font support will have the text drawn on the canvas + * using the Hershey font metrics. Even if the "enableFontSupport" option is true + * non-supporting browsers will still render with the Hershey font. + * + */ + $.jqplot.CanvasAxisLabelRenderer = function(options) { + // Group: Properties + + // prop: angle + // angle of text, measured clockwise from x axis. + this.angle = 0; + // name of the axis associated with this tick + this.axis; + // prop: show + // whether or not to show the tick (mark and label). + this.show = true; + // prop: showLabel + // whether or not to show the label. + this.showLabel = true; + // prop: label + // label for the axis. + this.label = ''; + // prop: fontFamily + // CSS spec for the font-family css attribute. + // Applies only to browsers supporting native font rendering in the + // canvas tag. Currently Mozilla 3.5 and Safari 4. + this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; + // prop: fontSize + // CSS spec for font size. + this.fontSize = '11pt'; + // prop: fontWeight + // CSS spec for fontWeight: normal, bold, bolder, lighter or a number 100 - 900 + this.fontWeight = 'normal'; + // prop: fontStretch + // Multiplier to condense or expand font width. + // Applies only to browsers which don't support canvas native font rendering. + this.fontStretch = 1.0; + // prop: textColor + // css spec for the color attribute. + this.textColor = '#666666'; + // prop: enableFontSupport + // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. + // If true, label will be drawn with canvas tag native support for fonts. + // If false, label will be drawn with Hershey font metrics. + this.enableFontSupport = true; + // prop: pt2px + // Point to pixel scaling factor, used for computing height of bounding box + // around a label. The labels text renderer has a default setting of 1.4, which + // should be suitable for most fonts. Leave as null to use default. If tops of + // letters appear clipped, increase this. If bounding box seems too big, decrease. + // This is an issue only with the native font renderering capabilities of Mozilla + // 3.5 and Safari 4 since they do not provide a method to determine the font height. + this.pt2px = null; + + this._elem; + this._ctx; + this._plotWidth; + this._plotHeight; + this._plotDimensions = {height:null, width:null}; + + $.extend(true, this, options); + + if (options.angle == null && this.axis != 'xaxis' && this.axis != 'x2axis') { + this.angle = -90; + } + + var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}; + if (this.pt2px) { + ropts.pt2px = this.pt2px; + } + + if (this.enableFontSupport) { + if ($.jqplot.support_canvas_text()) { + this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); + } + + else { + this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); + } + } + else { + this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); + } + }; + + $.jqplot.CanvasAxisLabelRenderer.prototype.init = function(options) { + $.extend(true, this, options); + this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}); + }; + + // return width along the x axis + // will check first to see if an element exists. + // if not, will return the computed text box width. + $.jqplot.CanvasAxisLabelRenderer.prototype.getWidth = function(ctx) { + if (this._elem) { + return this._elem.outerWidth(true); + } + else { + var tr = this._textRenderer; + var l = tr.getWidth(ctx); + var h = tr.getHeight(ctx); + var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l); + return w; + } + }; + + // return height along the y axis. + $.jqplot.CanvasAxisLabelRenderer.prototype.getHeight = function(ctx) { + if (this._elem) { + return this._elem.outerHeight(true); + } + else { + var tr = this._textRenderer; + var l = tr.getWidth(ctx); + var h = tr.getHeight(ctx); + var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l); + return w; + } + }; + + $.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad = function() { + var a = this.angle * Math.PI/180; + return a; + }; + + $.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx, plot) { + // Memory Leaks patch + if (this._elem) { + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); + } + + this._elem.emptyForce(); + this._elem = null; + } + + // create a canvas here, but can't draw on it untill it is appended + // to dom for IE compatability. + var elem = plot.canvasManager.getCanvas(); + + this._textRenderer.setText(this.label, ctx); + var w = this.getWidth(ctx); + var h = this.getHeight(ctx); + elem.width = w; + elem.height = h; + elem.style.width = w; + elem.style.height = h; + + elem = plot.canvasManager.initCanvas(elem); + + this._elem = $(elem); + this._elem.css({ position: 'absolute'}); + this._elem.addClass('jqplot-'+this.axis+'-label'); + + elem = null; + return this._elem; + }; + + $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() { + this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); + }; + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js new file mode 100644 index 0000000..ef91dd5 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.CanvasAxisLabelRenderer=function(b){this.angle=0;this.axis;this.show=true;this.showLabel=true;this.label="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="11pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);if(b.angle==null&&this.axis!="xaxis"&&this.axis!="x2axis"){this.angle=-90}var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){if(a.jqplot.support_canvas_text()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisLabelRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisLabelRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisLabelRenderer.prototype.draw=function(c,f){if(this._elem){if(a.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==undefined){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css({position:"absolute"});this._elem.addClass("jqplot-"+this.axis+"-label");e=null;return this._elem};a.jqplot.CanvasAxisLabelRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.js b/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.js new file mode 100644 index 0000000..b6120d0 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.js @@ -0,0 +1,253 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.CanvasAxisTickRenderer + * Renderer to draw axis ticks with a canvas element to support advanced + * featrues such as rotated text. This renderer uses a separate rendering engine + * to draw the text on the canvas. Two modes of rendering the text are available. + * If the browser has native font support for canvas fonts (currently Mozila 3.5 + * and Safari 4), you can enable text rendering with the canvas fillText method. + * You do so by setting the "enableFontSupport" option to true. + * + * Browsers lacking native font support will have the text drawn on the canvas + * using the Hershey font metrics. Even if the "enableFontSupport" option is true + * non-supporting browsers will still render with the Hershey font. + */ + $.jqplot.CanvasAxisTickRenderer = function(options) { + // Group: Properties + + // prop: mark + // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. + this.mark = 'outside'; + // prop: showMark + // whether or not to show the mark on the axis. + this.showMark = true; + // prop: showGridline + // whether or not to draw the gridline on the grid at this tick. + this.showGridline = true; + // prop: isMinorTick + // if this is a minor tick. + this.isMinorTick = false; + // prop: angle + // angle of text, measured clockwise from x axis. + this.angle = 0; + // prop: markSize + // Length of the tick marks in pixels. For 'cross' style, length + // will be stoked above and below axis, so total length will be twice this. + this.markSize = 4; + // prop: show + // whether or not to show the tick (mark and label). + this.show = true; + // prop: showLabel + // whether or not to show the label. + this.showLabel = true; + // prop: labelPosition + // 'auto', 'start', 'middle' or 'end'. + // Whether tick label should be positioned so the start, middle, or end + // of the tick mark. + this.labelPosition = 'auto'; + this.label = ''; + this.value = null; + this._styles = {}; + // prop: formatter + // A class of a formatter for the tick text. + // The default $.jqplot.DefaultTickFormatter uses sprintf. + this.formatter = $.jqplot.DefaultTickFormatter; + // prop: formatString + // string passed to the formatter. + this.formatString = ''; + // prop: prefix + // String to prepend to the tick label. + // Prefix is prepended to the formatted tick label. + this.prefix = ''; + // prop: fontFamily + // css spec for the font-family css attribute. + this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; + // prop: fontSize + // CSS spec for font size. + this.fontSize = '10pt'; + // prop: fontWeight + // CSS spec for fontWeight + this.fontWeight = 'normal'; + // prop: fontStretch + // Multiplier to condense or expand font width. + // Applies only to browsers which don't support canvas native font rendering. + this.fontStretch = 1.0; + // prop: textColor + // css spec for the color attribute. + this.textColor = '#666666'; + // prop: enableFontSupport + // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. + // If true, tick label will be drawn with canvas tag native support for fonts. + // If false, tick label will be drawn with Hershey font metrics. + this.enableFontSupport = true; + // prop: pt2px + // Point to pixel scaling factor, used for computing height of bounding box + // around a label. The labels text renderer has a default setting of 1.4, which + // should be suitable for most fonts. Leave as null to use default. If tops of + // letters appear clipped, increase this. If bounding box seems too big, decrease. + // This is an issue only with the native font renderering capabilities of Mozilla + // 3.5 and Safari 4 since they do not provide a method to determine the font height. + this.pt2px = null; + + this._elem; + this._ctx; + this._plotWidth; + this._plotHeight; + this._plotDimensions = {height:null, width:null}; + + $.extend(true, this, options); + + var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}; + if (this.pt2px) { + ropts.pt2px = this.pt2px; + } + + if (this.enableFontSupport) { + if ($.jqplot.support_canvas_text()) { + this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); + } + + else { + this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); + } + } + else { + this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); + } + }; + + $.jqplot.CanvasAxisTickRenderer.prototype.init = function(options) { + $.extend(true, this, options); + this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}); + }; + + // return width along the x axis + // will check first to see if an element exists. + // if not, will return the computed text box width. + $.jqplot.CanvasAxisTickRenderer.prototype.getWidth = function(ctx) { + if (this._elem) { + return this._elem.outerWidth(true); + } + else { + var tr = this._textRenderer; + var l = tr.getWidth(ctx); + var h = tr.getHeight(ctx); + var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l); + return w; + } + }; + + // return height along the y axis. + $.jqplot.CanvasAxisTickRenderer.prototype.getHeight = function(ctx) { + if (this._elem) { + return this._elem.outerHeight(true); + } + else { + var tr = this._textRenderer; + var l = tr.getWidth(ctx); + var h = tr.getHeight(ctx); + var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l); + return w; + } + }; + + // return top. + $.jqplot.CanvasAxisTickRenderer.prototype.getTop = function(ctx) { + if (this._elem) { + return this._elem.position().top; + } + else { + return null; + } + }; + + $.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad = function() { + var a = this.angle * Math.PI/180; + return a; + }; + + + $.jqplot.CanvasAxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { + this.value = value; + if (isMinor) { + this.isMinorTick = true; + } + return this; + }; + + $.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx, plot) { + if (!this.label) { + this.label = this.prefix + this.formatter(this.formatString, this.value); + } + + // Memory Leaks patch + if (this._elem) { + if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { + window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); + } + + this._elem.emptyForce(); + this._elem = null; + } + + // create a canvas here, but can't draw on it untill it is appended + // to dom for IE compatability. + + var elem = plot.canvasManager.getCanvas(); + + this._textRenderer.setText(this.label, ctx); + var w = this.getWidth(ctx); + var h = this.getHeight(ctx); + // canvases seem to need to have width and heigh attributes directly set. + elem.width = w; + elem.height = h; + elem.style.width = w; + elem.style.height = h; + elem.style.textAlign = 'left'; + elem.style.position = 'absolute'; + + elem = plot.canvasManager.initCanvas(elem); + + this._elem = $(elem); + this._elem.css(this._styles); + this._elem.addClass('jqplot-'+this.axis+'-tick'); + + elem = null; + return this._elem; + }; + + $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() { + this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); + }; + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js new file mode 100644 index 0000000..19875be --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.CanvasAxisTickRenderer=function(b){this.mark="outside";this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.angle=0;this.markSize=4;this.show=true;this.showLabel=true;this.labelPosition="auto";this.label="";this.value=null;this._styles={};this.formatter=a.jqplot.DefaultTickFormatter;this.formatString="";this.prefix="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="10pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){if(a.jqplot.support_canvas_text()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisTickRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisTickRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getTop=function(b){if(this._elem){return this._elem.position().top}else{return null}};a.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisTickRenderer.prototype.setTick=function(b,d,c){this.value=b;if(c){this.isMinorTick=true}return this};a.jqplot.CanvasAxisTickRenderer.prototype.draw=function(c,f){if(!this.label){this.label=this.prefix+this.formatter(this.formatString,this.value)}if(this._elem){if(a.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==undefined){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.textAlign="left";e.style.position="absolute";e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css(this._styles);this._elem.addClass("jqplot-"+this.axis+"-tick");e=null;return this._elem};a.jqplot.CanvasAxisTickRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasOverlay.js b/web/extensions/jqplot/plugins/jqplot.canvasOverlay.js new file mode 100644 index 0000000..4094413 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasOverlay.js @@ -0,0 +1,1021 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + var objCounter = 0; + // class: $.jqplot.CanvasOverlay + $.jqplot.CanvasOverlay = function(opts){ + var options = opts || {}; + this.options = { + show: $.jqplot.config.enablePlugins, + deferDraw: false + }; + // prop: objects + this.objects = []; + this.objectNames = []; + this.canvas = null; + this.markerRenderer = new $.jqplot.MarkerRenderer({style:'line'}); + this.markerRenderer.init(); + this.highlightObjectIndex = null; + if (options.objects) { + var objs = options.objects, + obj; + for (var i=0; i<objs.length; i++) { + obj = objs[i]; + for (var n in obj) { + switch (n) { + case 'line': + this.addLine(obj[n]); + break; + case 'horizontalLine': + this.addHorizontalLine(obj[n]); + break; + case 'dashedHorizontalLine': + this.addDashedHorizontalLine(obj[n]); + break; + case 'verticalLine': + this.addVerticalLine(obj[n]); + break; + case 'dashedVerticalLine': + this.addDashedVerticalLine(obj[n]); + break; + case 'rectangle': + this.addRectangle(obj[n]); + break; + default: + break; + } + } + } + } + $.extend(true, this.options, options); + }; + + // called with scope of a plot object + $.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) { + var options = opts || {}; + // add a canvasOverlay attribute to the plot + this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay); + }; + + + function LineBase() { + this.uid = null; + this.type = null; + this.gridStart = null; + this.gridStop = null; + this.tooltipWidthFactor = 0; + this.options = { + // prop: name + // Optional name for the overlay object. + // Can be later used to retrieve the object by name. + name: null, + // prop: show + // true to show (draw), false to not draw. + show: true, + // prop: lineWidth + // Width of the line. + lineWidth: 2, + // prop: lineCap + // Type of ending placed on the line ['round', 'butt', 'square'] + lineCap: 'round', + // prop: color + // color of the line + color: '#666666', + // prop: shadow + // whether or not to draw a shadow on the line + shadow: true, + // prop: shadowAngle + // Shadow angle in degrees + shadowAngle: 45, + // prop: shadowOffset + // Shadow offset from line in pixels + shadowOffset: 1, + // prop: shadowDepth + // Number of times shadow is stroked, each stroke offset shadowOffset from the last. + shadowDepth: 3, + // prop: shadowAlpha + // Alpha channel transparency of shadow. 0 = transparent. + shadowAlpha: '0.07', + // prop: xaxis + // X axis to use for positioning/scaling the line. + xaxis: 'xaxis', + // prop: yaxis + // Y axis to use for positioning/scaling the line. + yaxis: 'yaxis', + // prop: showTooltip + // Show a tooltip with data point values. + showTooltip: false, + // prop: showTooltipPrecision + // Controls how close to line cursor must be to show tooltip. + // Higher number = closer to line, lower number = farther from line. + // 1.0 = cursor must be over line. + showTooltipPrecision: 0.6, + // prop: tooltipLocation + // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' + tooltipLocation: 'nw', + // prop: fadeTooltip + // true = fade in/out tooltip, flase = show/hide tooltip + fadeTooltip: true, + // prop: tooltipFadeSpeed + // 'slow', 'def', 'fast', or number of milliseconds. + tooltipFadeSpeed: "fast", + // prop: tooltipOffset + // Pixel offset of tooltip from the highlight. + tooltipOffset: 4, + // prop: tooltipFormatString + // Format string passed the x and y values of the cursor on the line. + // e.g., 'Dogs: %.2f, Cats: %d'. + tooltipFormatString: '%d, %d' + }; + } + + + function Rectangle(options) { + LineBase.call(this); + this.type = 'rectangle'; + var opts = { + // prop: xmin + // x value for the start of the line, null to scale to axis min. + xmin: null, + // prop: xmax + // x value for the end of the line, null to scale to axis max. + xmax: null, + // prop xOffset + // offset ends of the line inside the grid. Number + xOffset: '6px', // number or string. Number interpreted as units, string as pixels. + xminOffset: null, + xmaxOffset: null, + + ymin: null, + ymax: null, + yOffset: '6px', // number or string. Number interpreted as units, string as pixels. + yminOffset: null, + ymaxOffset: null + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + Rectangle.prototype = new LineBase(); + Rectangle.prototype.constructor = Rectangle; + + + /** + * Class: Line + * A straight line. + */ + function Line(options) { + LineBase.call(this); + this.type = 'line'; + var opts = { + // prop: start + // [x, y] coordinates for the start of the line. + start: [], + // prop: stop + // [x, y] coordinates for the end of the line. + stop: [] + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + Line.prototype = new LineBase(); + Line.prototype.constructor = Line; + + + /** + * Class: HorizontalLine + * A straight horizontal line. + */ + function HorizontalLine(options) { + LineBase.call(this); + this.type = 'horizontalLine'; + var opts = { + // prop: y + // y value to position the line + y: null, + // prop: xmin + // x value for the start of the line, null to scale to axis min. + xmin: null, + // prop: xmax + // x value for the end of the line, null to scale to axis max. + xmax: null, + // prop xOffset + // offset ends of the line inside the grid. Number + xOffset: '6px', // number or string. Number interpreted as units, string as pixels. + xminOffset: null, + xmaxOffset: null + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + HorizontalLine.prototype = new LineBase(); + HorizontalLine.prototype.constructor = HorizontalLine; + + + /** + * Class: DashedHorizontalLine + * A straight dashed horizontal line. + */ + function DashedHorizontalLine(options) { + LineBase.call(this); + this.type = 'dashedHorizontalLine'; + var opts = { + y: null, + xmin: null, + xmax: null, + xOffset: '6px', // number or string. Number interpreted as units, string as pixels. + xminOffset: null, + xmaxOffset: null, + // prop: dashPattern + // Array of line, space settings in pixels. + // Default is 8 pixel of line, 8 pixel of space. + // Note, limit to a 2 element array b/c of bug with higher order arrays. + dashPattern: [8,8] + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + DashedHorizontalLine.prototype = new LineBase(); + DashedHorizontalLine.prototype.constructor = DashedHorizontalLine; + + + /** + * Class: VerticalLine + * A straight vertical line. + */ + function VerticalLine(options) { + LineBase.call(this); + this.type = 'verticalLine'; + var opts = { + x: null, + ymin: null, + ymax: null, + yOffset: '6px', // number or string. Number interpreted as units, string as pixels. + yminOffset: null, + ymaxOffset: null + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + VerticalLine.prototype = new LineBase(); + VerticalLine.prototype.constructor = VerticalLine; + + + /** + * Class: DashedVerticalLine + * A straight dashed vertical line. + */ + function DashedVerticalLine(options) { + LineBase.call(this); + this.type = 'dashedVerticalLine'; + this.start = null; + this.stop = null; + var opts = { + x: null, + ymin: null, + ymax: null, + yOffset: '6px', // number or string. Number interpreted as units, string as pixels. + yminOffset: null, + ymaxOffset: null, + // prop: dashPattern + // Array of line, space settings in pixels. + // Default is 8 pixel of line, 8 pixel of space. + // Note, limit to a 2 element array b/c of bug with higher order arrays. + dashPattern: [8,8] + }; + $.extend(true, this.options, opts, options); + + if (this.options.showTooltipPrecision < 0.01) { + this.options.showTooltipPrecision = 0.01; + } + } + + DashedVerticalLine.prototype = new LineBase(); + DashedVerticalLine.prototype.constructor = DashedVerticalLine; + + $.jqplot.CanvasOverlay.prototype.addLine = function(opts) { + var line = new Line(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) { + var line = new HorizontalLine(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) { + var line = new DashedHorizontalLine(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) { + var line = new VerticalLine(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) { + var line = new DashedVerticalLine(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.addRectangle = function(opts) { + var line = new Rectangle(opts); + line.uid = objCounter++; + this.objects.push(line); + this.objectNames.push(line.options.name); + }; + + $.jqplot.CanvasOverlay.prototype.removeObject = function(idx) { + // check if integer, remove by index + if ($.type(idx) == 'number') { + this.objects.splice(idx, 1); + this.objectNames.splice(idx, 1); + } + // if string, remove by name + else { + var id = $.inArray(idx, this.objectNames); + if (id != -1) { + this.objects.splice(id, 1); + this.objectNames.splice(id, 1); + } + } + }; + + $.jqplot.CanvasOverlay.prototype.getObject = function(idx) { + // check if integer, remove by index + if ($.type(idx) == 'number') { + return this.objects[idx]; + } + // if string, remove by name + else { + var id = $.inArray(idx, this.objectNames); + if (id != -1) { + return this.objects[id]; + } + } + }; + + // Set get as alias for getObject. + $.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject; + + $.jqplot.CanvasOverlay.prototype.clear = function(plot) { + this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); + }; + + $.jqplot.CanvasOverlay.prototype.draw = function(plot) { + var obj, + objs = this.objects, + mr = this.markerRenderer, + start, + stop; + if (this.options.show) { + this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); + for (var k=0; k<objs.length; k++) { + obj = objs[k]; + var opts = $.extend(true, {}, obj.options); + if (obj.options.show) { + // style and shadow properties should be set before + // every draw of marker renderer. + mr.shadow = obj.options.shadow; + obj.tooltipWidthFactor = obj.options.lineWidth / obj.options.showTooltipPrecision; + switch (obj.type) { + case 'line': + // style and shadow properties should be set before + // every draw of marker renderer. + mr.style = 'line'; + opts.closePath = false; + start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])]; + stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])]; + obj.gridStart = start; + obj.gridStop = stop; + mr.draw(start, stop, this.canvas._ctx, opts); + break; + case 'horizontalLine': + + // style and shadow properties should be set before + // every draw of marker renderer. + if (obj.options.y != null) { + mr.style = 'line'; + opts.closePath = false; + var xaxis = plot.axes[obj.options.xaxis], + xstart, + xstop, + y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), + xminoff = obj.options.xminOffset || obj.options.xOffset, + xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; + if (obj.options.xmin != null) { + xstart = xaxis.series_u2p(obj.options.xmin); + } + else if (xminoff != null) { + if ($.type(xminoff) == "number") { + xstart = xaxis.series_u2p(xaxis.min + xminoff); + } + else if ($.type(xminoff) == "string") { + xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); + } + } + if (obj.options.xmax != null) { + xstop = xaxis.series_u2p(obj.options.xmax); + } + else if (xmaxoff != null) { + if ($.type(xmaxoff) == "number") { + xstop = xaxis.series_u2p(xaxis.max - xmaxoff); + } + else if ($.type(xmaxoff) == "string") { + xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); + } + } + if (xstop != null && xstart != null) { + obj.gridStart = [xstart, y]; + obj.gridStop = [xstop, y]; + mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts); + } + } + break; + + case 'dashedHorizontalLine': + + var dashPat = obj.options.dashPattern; + var dashPatLen = 0; + for (var i=0; i<dashPat.length; i++) { + dashPatLen += dashPat[i]; + } + + // style and shadow properties should be set before + // every draw of marker renderer. + if (obj.options.y != null) { + mr.style = 'line'; + opts.closePath = false; + var xaxis = plot.axes[obj.options.xaxis], + xstart, + xstop, + y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), + xminoff = obj.options.xminOffset || obj.options.xOffset, + xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; + if (obj.options.xmin != null) { + xstart = xaxis.series_u2p(obj.options.xmin); + } + else if (xminoff != null) { + if ($.type(xminoff) == "number") { + xstart = xaxis.series_u2p(xaxis.min + xminoff); + } + else if ($.type(xminoff) == "string") { + xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); + } + } + if (obj.options.xmax != null) { + xstop = xaxis.series_u2p(obj.options.xmax); + } + else if (xmaxoff != null) { + if ($.type(xmaxoff) == "number") { + xstop = xaxis.series_u2p(xaxis.max - xmaxoff); + } + else if ($.type(xmaxoff) == "string") { + xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); + } + } + if (xstop != null && xstart != null) { + obj.gridStart = [xstart, y]; + obj.gridStop = [xstop, y]; + var numDash = Math.ceil((xstop - xstart)/dashPatLen); + var b=xstart, e; + for (var i=0; i<numDash; i++) { + for (var j=0; j<dashPat.length; j+=2) { + e = b+dashPat[j]; + mr.draw([b, y], [e, y], this.canvas._ctx, opts); + b += dashPat[j]; + if (j < dashPat.length-1) { + b += dashPat[j+1]; + } + } + } + } + } + break; + + case 'verticalLine': + + // style and shadow properties should be set before + // every draw of marker renderer. + if (obj.options.x != null) { + mr.style = 'line'; + opts.closePath = false; + var yaxis = plot.axes[obj.options.yaxis], + ystart, + ystop, + x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), + yminoff = obj.options.yminOffset || obj.options.yOffset, + ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; + if (obj.options.ymin != null) { + ystart = yaxis.series_u2p(obj.options.ymin); + } + else if (yminoff != null) { + if ($.type(yminoff) == "number") { + ystart = yaxis.series_u2p(yaxis.min - yminoff); + } + else if ($.type(yminoff) == "string") { + ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); + } + } + if (obj.options.ymax != null) { + ystop = yaxis.series_u2p(obj.options.ymax); + } + else if (ymaxoff != null) { + if ($.type(ymaxoff) == "number") { + ystop = yaxis.series_u2p(yaxis.max + ymaxoff); + } + else if ($.type(ymaxoff) == "string") { + ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); + } + } + if (ystop != null && ystart != null) { + obj.gridStart = [x, ystart]; + obj.gridStop = [x, ystop]; + mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts); + } + } + break; + + case 'dashedVerticalLine': + + var dashPat = obj.options.dashPattern; + var dashPatLen = 0; + for (var i=0; i<dashPat.length; i++) { + dashPatLen += dashPat[i]; + } + + // style and shadow properties should be set before + // every draw of marker renderer. + if (obj.options.x != null) { + mr.style = 'line'; + opts.closePath = false; + var yaxis = plot.axes[obj.options.yaxis], + ystart, + ystop, + x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), + yminoff = obj.options.yminOffset || obj.options.yOffset, + ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; + if (obj.options.ymin != null) { + ystart = yaxis.series_u2p(obj.options.ymin); + } + else if (yminoff != null) { + if ($.type(yminoff) == "number") { + ystart = yaxis.series_u2p(yaxis.min - yminoff); + } + else if ($.type(yminoff) == "string") { + ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); + } + } + if (obj.options.ymax != null) { + ystop = yaxis.series_u2p(obj.options.ymax); + } + else if (ymaxoff != null) { + if ($.type(ymaxoff) == "number") { + ystop = yaxis.series_u2p(yaxis.max + ymaxoff); + } + else if ($.type(ymaxoff) == "string") { + ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); + } + } + + + if (ystop != null && ystart != null) { + obj.gridStart = [x, ystart]; + obj.gridStop = [x, ystop]; + var numDash = Math.ceil((ystart - ystop)/dashPatLen); + var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0; + var b=ystart, e, bs, es; + for (var i=0; i<numDash; i++) { + for (var j=0; j<dashPat.length; j+=2) { + e = b - dashPat[j]; + if (e < ystop) { + e = ystop; + } + if (b < ystop) { + b = ystop; + } + // es = e; + // if (i == 0) { + // es += firstDashAdjust; + // } + mr.draw([x, b], [x, e], this.canvas._ctx, opts); + b -= dashPat[j]; + if (j < dashPat.length-1) { + b -= dashPat[j+1]; + } + } + } + } + } + break; + + case 'rectangle': + // style and shadow properties should be set before + // every draw of marker renderer. + mr.style = 'line'; + opts.closePath = true; + + var xaxis = plot.axes[obj.options.xaxis], + xstart, + xstop, + y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), + xminoff = obj.options.xminOffset || obj.options.xOffset, + xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; + if (obj.options.xmin != null) { + xstart = xaxis.series_u2p(obj.options.xmin); + } + else if (xminoff != null) { + if ($.type(xminoff) == "number") { + xstart = xaxis.series_u2p(xaxis.min + xminoff); + } + else if ($.type(xminoff) == "string") { + xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); + } + } + if (obj.options.xmax != null) { + xstop = xaxis.series_u2p(obj.options.xmax); + } + else if (xmaxoff != null) { + if ($.type(xmaxoff) == "number") { + xstop = xaxis.series_u2p(xaxis.max - xmaxoff); + } + else if ($.type(xmaxoff) == "string") { + xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); + } + } + + var yaxis = plot.axes[obj.options.yaxis], + ystart, + ystop, + x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), + yminoff = obj.options.yminOffset || obj.options.yOffset, + ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; + if (obj.options.ymin != null) { + ystart = yaxis.series_u2p(obj.options.ymin); + } + else if (yminoff != null) { + if ($.type(yminoff) == "number") { + ystart = yaxis.series_u2p(yaxis.min - yminoff); + } + else if ($.type(yminoff) == "string") { + ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); + } + } + if (obj.options.ymax != null) { + ystop = yaxis.series_u2p(obj.options.ymax); + } + else if (ymaxoff != null) { + if ($.type(ymaxoff) == "number") { + ystop = yaxis.series_u2p(yaxis.max + ymaxoff); + } + else if ($.type(ymaxoff) == "string") { + ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); + } + } + + + if (xstop != null && xstart != null && ystop != null && ystart != null) { + obj.gridStart = [xstart, ystart]; + obj.gridStop = [xstop, ystop]; + + this.canvas._ctx.fillStyle = obj.options.color; + this.canvas._ctx.fillRect(xstart, ystart, xstop - xstart, ystop - ystart); + } + break; + + default: + break; + } + } + } + } + }; + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + $.jqplot.CanvasOverlay.postPlotDraw = function() { + var co = this.plugins.canvasOverlay; + // Memory Leaks patch + if (co && co.highlightCanvas) { + co.highlightCanvas.resetCanvas(); + co.highlightCanvas = null; + } + co.canvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(co.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this)); + co.canvas.setContext(); + if (!co.deferDraw) { + co.draw(this); + } + + var elem = document.createElement('div'); + co._tooltipElem = $(elem); + elem = null; + co._tooltipElem.addClass('jqplot-canvasOverlay-tooltip'); + co._tooltipElem.css({position:'absolute', display:'none'}); + + this.eventCanvas._elem.before(co._tooltipElem); + this.eventCanvas._elem.bind('mouseleave', { elem: co._tooltipElem }, function (ev) { ev.data.elem.hide(); }); + + var co = null; + }; + + + function showTooltip(plot, obj, gridpos, datapos) { + var co = plot.plugins.canvasOverlay; + var elem = co._tooltipElem; + + var opts = obj.options, x, y; + + elem.html($.jqplot.sprintf(opts.tooltipFormatString, datapos[0], datapos[1])); + + switch (opts.tooltipLocation) { + case 'nw': + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); + break; + case 'n': + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2; + y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); + break; + case 'ne': + x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); + break; + case 'e': + x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + case 'se': + x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; + break; + case 's': + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2; + y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; + break; + case 'sw': + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; + break; + case 'w': + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + default: // same as 'nw' + x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; + y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); + break; + } + + elem.css('left', x); + elem.css('top', y); + if (opts.fadeTooltip) { + // Fix for stacked up animations. Thnanks Trevor! + elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed); + } + else { + elem.show(); + } + elem = null; + } + + + function isNearLine(point, lstart, lstop, width) { + // r is point to test, p and q are end points. + var rx = point[0]; + var ry = point[1]; + var px = Math.round(lstop[0]); + var py = Math.round(lstop[1]); + var qx = Math.round(lstart[0]); + var qy = Math.round(lstart[1]); + + var l = Math.sqrt(Math.pow(px-qx, 2) + Math.pow(py-qy, 2)); + + // scale error term by length of line. + var eps = width*l; + var res = Math.abs((qx-px) * (ry-py) - (qy-py) * (rx-px)); + var ret = (res < eps) ? true : false; + return ret; + } + + function isNearRectangle(point, lstart, lstop, width) { + // r is point to test, p and q are end points. + var rx = point[0]; + var ry = point[1]; + var px = Math.round(lstop[0]); + var py = Math.round(lstop[1]); + var qx = Math.round(lstart[0]); + var qy = Math.round(lstart[1]); + + var temp; + if (px > qx) { temp = px; px = qx; qx = temp; } + if (py > qy) { temp = py; py = qy; qy = temp; } + + var ret = (rx >= px && rx <= qx && ry >= py && ry <= qy); + + return ret; + } + + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + var co = plot.plugins.canvasOverlay; + var objs = co.objects; + var l = objs.length; + var obj, haveHighlight=false; + var elem; + for (var i=0; i<l; i++) { + obj = objs[i]; + if (obj.options.showTooltip) { + var n; + if (obj.type === 'rectangle') { + n = isNearRectangle([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor); + } else { + n = isNearLine([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor); + } + datapos = [plot.axes[obj.options.xaxis].series_p2u(gridpos.x), plot.axes[obj.options.yaxis].series_p2u(gridpos.y)]; + + // cases: + // near line, no highlighting + // near line, highliting on this line + // near line, highlighting another line + // not near any line, highlighting + // not near any line, no highlighting + + // near line, not currently highlighting + if (n && co.highlightObjectIndex == null) { + switch (obj.type) { + case 'line': + showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); + break; + + case 'horizontalLine': + case 'dashedHorizontalLine': + showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); + break; + + case 'verticalLine': + case 'dashedVerticalLine': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + case 'rectangle': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + default: + break; + } + co.highlightObjectIndex = i; + haveHighlight = true; + break; + } + + // near line, highlighting another line. + else if (n && co.highlightObjectIndex !== i) { + // turn off tooltip. + elem = co._tooltipElem; + if (obj.fadeTooltip) { + elem.fadeOut(obj.tooltipFadeSpeed); + } + else { + elem.hide(); + } + + // turn on right tooltip. + switch (obj.type) { + case 'line': + showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); + break; + + case 'horizontalLine': + case 'dashedHorizontalLine': + showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); + break; + + case 'verticalLine': + case 'dashedVerticalLine': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + case 'rectangle': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + default: + break; + } + + co.highlightObjectIndex = i; + haveHighlight = true; + break; + } + + // near line, already highlighting this line, update + else if (n) { + switch (obj.type) { + case 'line': + showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); + break; + + case 'horizontalLine': + case 'dashedHorizontalLine': + showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); + break; + + case 'verticalLine': + case 'dashedVerticalLine': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + case 'rectangle': + showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); + break; + + default: + break; + } + + haveHighlight = true; + break; + } + } + } + + // check if we are highlighting and not near a line, turn it off. + if (!haveHighlight && co.highlightObjectIndex !== null) { + elem = co._tooltipElem; + obj = co.getObject(co.highlightObjectIndex); + if (obj.fadeTooltip) { + elem.fadeOut(obj.tooltipFadeSpeed); + } + else { + elem.hide(); + } + co.highlightObjectIndex = null; + } + } + + $.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit); + $.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw); + $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasOverlay.min.js b/web/extensions/jqplot/plugins/jqplot.canvasOverlay.min.js new file mode 100644 index 0000000..fa469ad --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasOverlay.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(d){var f=0;d.jqplot.CanvasOverlay=function(q){var o=q||{};this.options={show:d.jqplot.config.enablePlugins,deferDraw:false};this.objects=[];this.objectNames=[];this.canvas=null;this.markerRenderer=new d.jqplot.MarkerRenderer({style:"line"});this.markerRenderer.init();this.highlightObjectIndex=null;if(o.objects){var s=o.objects,r;for(var p=0;p<s.length;p++){r=s[p];for(var t in r){switch(t){case"line":this.addLine(r[t]);break;case"horizontalLine":this.addHorizontalLine(r[t]);break;case"dashedHorizontalLine":this.addDashedHorizontalLine(r[t]);break;case"verticalLine":this.addVerticalLine(r[t]);break;case"dashedVerticalLine":this.addDashedVerticalLine(r[t]);break;case"rectangle":this.addRectangle(r[t]);break;default:break}}}}d.extend(true,this.options,o)};d.jqplot.CanvasOverlay.postPlotInit=function(q,p,o){var n=o||{};this.plugins.canvasOverlay=new d.jqplot.CanvasOverlay(n.canvasOverlay)};function i(){this.uid=null;this.type=null;this.gridStart=null;this.gridStop=null;this.tooltipWidthFactor=0;this.options={name:null,show:true,lineWidth:2,lineCap:"round",color:"#666666",shadow:true,shadowAngle:45,shadowOffset:1,shadowDepth:3,shadowAlpha:"0.07",xaxis:"xaxis",yaxis:"yaxis",showTooltip:false,showTooltipPrecision:0.6,tooltipLocation:"nw",fadeTooltip:true,tooltipFadeSpeed:"fast",tooltipOffset:4,tooltipFormatString:"%d, %d"}}function m(n){i.call(this);this.type="rectangle";var o={xmin:null,xmax:null,xOffset:"6px",xminOffset:null,xmaxOffset:null,ymin:null,ymax:null,yOffset:"6px",yminOffset:null,ymaxOffset:null};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}m.prototype=new i();m.prototype.constructor=m;function b(n){i.call(this);this.type="line";var o={start:[],stop:[]};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}b.prototype=new i();b.prototype.constructor=b;function e(n){i.call(this);this.type="horizontalLine";var o={y:null,xmin:null,xmax:null,xOffset:"6px",xminOffset:null,xmaxOffset:null};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}e.prototype=new i();e.prototype.constructor=e;function j(n){i.call(this);this.type="dashedHorizontalLine";var o={y:null,xmin:null,xmax:null,xOffset:"6px",xminOffset:null,xmaxOffset:null,dashPattern:[8,8]};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}j.prototype=new i();j.prototype.constructor=j;function c(n){i.call(this);this.type="verticalLine";var o={x:null,ymin:null,ymax:null,yOffset:"6px",yminOffset:null,ymaxOffset:null};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}c.prototype=new i();c.prototype.constructor=c;function l(n){i.call(this);this.type="dashedVerticalLine";this.start=null;this.stop=null;var o={x:null,ymin:null,ymax:null,yOffset:"6px",yminOffset:null,ymaxOffset:null,dashPattern:[8,8]};d.extend(true,this.options,o,n);if(this.options.showTooltipPrecision<0.01){this.options.showTooltipPrecision=0.01}}l.prototype=new i();l.prototype.constructor=l;d.jqplot.CanvasOverlay.prototype.addLine=function(o){var n=new b(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.addHorizontalLine=function(o){var n=new e(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine=function(o){var n=new j(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.addVerticalLine=function(o){var n=new c(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.addDashedVerticalLine=function(o){var n=new l(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.addRectangle=function(o){var n=new m(o);n.uid=f++;this.objects.push(n);this.objectNames.push(n.options.name)};d.jqplot.CanvasOverlay.prototype.removeObject=function(n){if(d.type(n)=="number"){this.objects.splice(n,1);this.objectNames.splice(n,1)}else{var o=d.inArray(n,this.objectNames);if(o!=-1){this.objects.splice(o,1);this.objectNames.splice(o,1)}}};d.jqplot.CanvasOverlay.prototype.getObject=function(n){if(d.type(n)=="number"){return this.objects[n]}else{var o=d.inArray(n,this.objectNames);if(o!=-1){return this.objects[o]}}};d.jqplot.CanvasOverlay.prototype.get=d.jqplot.CanvasOverlay.prototype.getObject;d.jqplot.CanvasOverlay.prototype.clear=function(n){this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(),this.canvas.getHeight())};d.jqplot.CanvasOverlay.prototype.draw=function(K){var A,v=this.objects,F=this.markerRenderer,s,G;if(this.options.show){this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(),this.canvas.getHeight());for(var H=0;H<v.length;H++){A=v[H];var B=d.extend(true,{},A.options);if(A.options.show){F.shadow=A.options.shadow;A.tooltipWidthFactor=A.options.lineWidth/A.options.showTooltipPrecision;switch(A.type){case"line":F.style="line";B.closePath=false;s=[K.axes[A.options.xaxis].series_u2p(A.options.start[0]),K.axes[A.options.yaxis].series_u2p(A.options.start[1])];G=[K.axes[A.options.xaxis].series_u2p(A.options.stop[0]),K.axes[A.options.yaxis].series_u2p(A.options.stop[1])];A.gridStart=s;A.gridStop=G;F.draw(s,G,this.canvas._ctx,B);break;case"horizontalLine":if(A.options.y!=null){F.style="line";B.closePath=false;var p=K.axes[A.options.xaxis],S,L,w=K.axes[A.options.yaxis].series_u2p(A.options.y),I=A.options.xminOffset||A.options.xOffset,t=A.options.xmaxOffset||A.options.xOffset;if(A.options.xmin!=null){S=p.series_u2p(A.options.xmin)}else{if(I!=null){if(d.type(I)=="number"){S=p.series_u2p(p.min+I)}else{if(d.type(I)=="string"){S=p.series_u2p(p.min)+parseFloat(I)}}}}if(A.options.xmax!=null){L=p.series_u2p(A.options.xmax)}else{if(t!=null){if(d.type(t)=="number"){L=p.series_u2p(p.max-t)}else{if(d.type(t)=="string"){L=p.series_u2p(p.max)-parseFloat(t)}}}}if(L!=null&&S!=null){A.gridStart=[S,w];A.gridStop=[L,w];F.draw([S,w],[L,w],this.canvas._ctx,B)}}break;case"dashedHorizontalLine":var o=A.options.dashPattern;var E=0;for(var M=0;M<o.length;M++){E+=o[M]}if(A.options.y!=null){F.style="line";B.closePath=false;var p=K.axes[A.options.xaxis],S,L,w=K.axes[A.options.yaxis].series_u2p(A.options.y),I=A.options.xminOffset||A.options.xOffset,t=A.options.xmaxOffset||A.options.xOffset;if(A.options.xmin!=null){S=p.series_u2p(A.options.xmin)}else{if(I!=null){if(d.type(I)=="number"){S=p.series_u2p(p.min+I)}else{if(d.type(I)=="string"){S=p.series_u2p(p.min)+parseFloat(I)}}}}if(A.options.xmax!=null){L=p.series_u2p(A.options.xmax)}else{if(t!=null){if(d.type(t)=="number"){L=p.series_u2p(p.max-t)}else{if(d.type(t)=="string"){L=p.series_u2p(p.max)-parseFloat(t)}}}}if(L!=null&&S!=null){A.gridStart=[S,w];A.gridStop=[L,w];var r=Math.ceil((L-S)/E);var Q=S,O;for(var M=0;M<r;M++){for(var J=0;J<o.length;J+=2){O=Q+o[J];F.draw([Q,w],[O,w],this.canvas._ctx,B);Q+=o[J];if(J<o.length-1){Q+=o[J+1]}}}}}break;case"verticalLine":if(A.options.x!=null){F.style="line";B.closePath=false;var N=K.axes[A.options.yaxis],n,u,z=K.axes[A.options.xaxis].series_u2p(A.options.x),D=A.options.yminOffset||A.options.yOffset,q=A.options.ymaxOffset||A.options.yOffset;if(A.options.ymin!=null){n=N.series_u2p(A.options.ymin)}else{if(D!=null){if(d.type(D)=="number"){n=N.series_u2p(N.min-D)}else{if(d.type(D)=="string"){n=N.series_u2p(N.min)-parseFloat(D)}}}}if(A.options.ymax!=null){u=N.series_u2p(A.options.ymax)}else{if(q!=null){if(d.type(q)=="number"){u=N.series_u2p(N.max+q)}else{if(d.type(q)=="string"){u=N.series_u2p(N.max)+parseFloat(q)}}}}if(u!=null&&n!=null){A.gridStart=[z,n];A.gridStop=[z,u];F.draw([z,n],[z,u],this.canvas._ctx,B)}}break;case"dashedVerticalLine":var o=A.options.dashPattern;var E=0;for(var M=0;M<o.length;M++){E+=o[M]}if(A.options.x!=null){F.style="line";B.closePath=false;var N=K.axes[A.options.yaxis],n,u,z=K.axes[A.options.xaxis].series_u2p(A.options.x),D=A.options.yminOffset||A.options.yOffset,q=A.options.ymaxOffset||A.options.yOffset;if(A.options.ymin!=null){n=N.series_u2p(A.options.ymin)}else{if(D!=null){if(d.type(D)=="number"){n=N.series_u2p(N.min-D)}else{if(d.type(D)=="string"){n=N.series_u2p(N.min)-parseFloat(D)}}}}if(A.options.ymax!=null){u=N.series_u2p(A.options.ymax)}else{if(q!=null){if(d.type(q)=="number"){u=N.series_u2p(N.max+q)}else{if(d.type(q)=="string"){u=N.series_u2p(N.max)+parseFloat(q)}}}}if(u!=null&&n!=null){A.gridStart=[z,n];A.gridStop=[z,u];var r=Math.ceil((n-u)/E);var C=((r*E)-(n-u))/2;var Q=n,O,P,R;for(var M=0;M<r;M++){for(var J=0;J<o.length;J+=2){O=Q-o[J];if(O<u){O=u}if(Q<u){Q=u}F.draw([z,Q],[z,O],this.canvas._ctx,B);Q-=o[J];if(J<o.length-1){Q-=o[J+1]}}}}}break;case"rectangle":F.style="line";B.closePath=true;var p=K.axes[A.options.xaxis],S,L,w=K.axes[A.options.yaxis].series_u2p(A.options.y),I=A.options.xminOffset||A.options.xOffset,t=A.options.xmaxOffset||A.options.xOffset;if(A.options.xmin!=null){S=p.series_u2p(A.options.xmin)}else{if(I!=null){if(d.type(I)=="number"){S=p.series_u2p(p.min+I)}else{if(d.type(I)=="string"){S=p.series_u2p(p.min)+parseFloat(I)}}}}if(A.options.xmax!=null){L=p.series_u2p(A.options.xmax)}else{if(t!=null){if(d.type(t)=="number"){L=p.series_u2p(p.max-t)}else{if(d.type(t)=="string"){L=p.series_u2p(p.max)-parseFloat(t)}}}}var N=K.axes[A.options.yaxis],n,u,z=K.axes[A.options.xaxis].series_u2p(A.options.x),D=A.options.yminOffset||A.options.yOffset,q=A.options.ymaxOffset||A.options.yOffset;if(A.options.ymin!=null){n=N.series_u2p(A.options.ymin)}else{if(D!=null){if(d.type(D)=="number"){n=N.series_u2p(N.min-D)}else{if(d.type(D)=="string"){n=N.series_u2p(N.min)-parseFloat(D)}}}}if(A.options.ymax!=null){u=N.series_u2p(A.options.ymax)}else{if(q!=null){if(d.type(q)=="number"){u=N.series_u2p(N.max+q)}else{if(d.type(q)=="string"){u=N.series_u2p(N.max)+parseFloat(q)}}}}if(L!=null&&S!=null&&u!=null&&n!=null){A.gridStart=[S,n];A.gridStop=[L,u];this.canvas._ctx.fillStyle=A.options.color;this.canvas._ctx.fillRect(S,n,L-S,u-n)}break;default:break}}}}};d.jqplot.CanvasOverlay.postPlotDraw=function(){var o=this.plugins.canvasOverlay;if(o&&o.highlightCanvas){o.highlightCanvas.resetCanvas();o.highlightCanvas=null}o.canvas=new d.jqplot.GenericCanvas();this.eventCanvas._elem.before(o.canvas.createElement(this._gridPadding,"jqplot-overlayCanvas-canvas",this._plotDimensions,this));o.canvas.setContext();if(!o.deferDraw){o.draw(this)}var n=document.createElement("div");o._tooltipElem=d(n);n=null;o._tooltipElem.addClass("jqplot-canvasOverlay-tooltip");o._tooltipElem.css({position:"absolute",display:"none"});this.eventCanvas._elem.before(o._tooltipElem);this.eventCanvas._elem.bind("mouseleave",{elem:o._tooltipElem},function(p){p.data.elem.hide()});var o=null};function k(s,q,r,p){var u=s.plugins.canvasOverlay;var o=u._tooltipElem;var n=q.options,v,t;o.html(d.jqplot.sprintf(n.tooltipFormatString,p[0],p[1]));switch(n.tooltipLocation){case"nw":v=r[0]+s._gridPadding.left-o.outerWidth(true)-n.tooltipOffset;t=r[1]+s._gridPadding.top-n.tooltipOffset-o.outerHeight(true);break;case"n":v=r[0]+s._gridPadding.left-o.outerWidth(true)/2;t=r[1]+s._gridPadding.top-n.tooltipOffset-o.outerHeight(true);break;case"ne":v=r[0]+s._gridPadding.left+n.tooltipOffset;t=r[1]+s._gridPadding.top-n.tooltipOffset-o.outerHeight(true);break;case"e":v=r[0]+s._gridPadding.left+n.tooltipOffset;t=r[1]+s._gridPadding.top-o.outerHeight(true)/2;break;case"se":v=r[0]+s._gridPadding.left+n.tooltipOffset;t=r[1]+s._gridPadding.top+n.tooltipOffset;break;case"s":v=r[0]+s._gridPadding.left-o.outerWidth(true)/2;t=r[1]+s._gridPadding.top+n.tooltipOffset;break;case"sw":v=r[0]+s._gridPadding.left-o.outerWidth(true)-n.tooltipOffset;t=r[1]+s._gridPadding.top+n.tooltipOffset;break;case"w":v=r[0]+s._gridPadding.left-o.outerWidth(true)-n.tooltipOffset;t=r[1]+s._gridPadding.top-o.outerHeight(true)/2;break;default:v=r[0]+s._gridPadding.left-o.outerWidth(true)-n.tooltipOffset;t=r[1]+s._gridPadding.top-n.tooltipOffset-o.outerHeight(true);break}o.css("left",v);o.css("top",t);if(n.fadeTooltip){o.stop(true,true).fadeIn(n.tooltipFadeSpeed)}else{o.show()}o=null}function h(z,q,s,p){var o=z[0];var n=z[1];var y=Math.round(s[0]);var x=Math.round(s[1]);var u=Math.round(q[0]);var t=Math.round(q[1]);var r=Math.sqrt(Math.pow(y-u,2)+Math.pow(x-t,2));var A=p*r;var w=Math.abs((u-y)*(n-x)-(t-x)*(o-y));var v=(w<A)?true:false;return v}function g(x,q,r,p){var o=x[0];var n=x[1];var w=Math.round(r[0]);var v=Math.round(r[1]);var t=Math.round(q[0]);var s=Math.round(q[1]);var y;if(w>t){y=w;w=t;t=y}if(v>s){y=v;v=s;s=y}var u=(o>=w&&o<=t&&n>=v&&n<=s);return u}function a(z,w,r,A,x){var y=x.plugins.canvasOverlay;var v=y.objects;var s=v.length;var u,o=false;var q;for(var t=0;t<s;t++){u=v[t];if(u.options.showTooltip){var p;if(u.type==="rectangle"){p=g([w.x,w.y],u.gridStart,u.gridStop,u.tooltipWidthFactor)}else{p=h([w.x,w.y],u.gridStart,u.gridStop,u.tooltipWidthFactor)}r=[x.axes[u.options.xaxis].series_p2u(w.x),x.axes[u.options.yaxis].series_p2u(w.y)];if(p&&y.highlightObjectIndex==null){switch(u.type){case"line":k(x,u,[w.x,w.y],r);break;case"horizontalLine":case"dashedHorizontalLine":k(x,u,[w.x,u.gridStart[1]],[r[0],u.options.y]);break;case"verticalLine":case"dashedVerticalLine":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;case"rectangle":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;default:break}y.highlightObjectIndex=t;o=true;break}else{if(p&&y.highlightObjectIndex!==t){q=y._tooltipElem;if(u.fadeTooltip){q.fadeOut(u.tooltipFadeSpeed)}else{q.hide()}switch(u.type){case"line":k(x,u,[w.x,w.y],r);break;case"horizontalLine":case"dashedHorizontalLine":k(x,u,[w.x,u.gridStart[1]],[r[0],u.options.y]);break;case"verticalLine":case"dashedVerticalLine":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;case"rectangle":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;default:break}y.highlightObjectIndex=t;o=true;break}else{if(p){switch(u.type){case"line":k(x,u,[w.x,w.y],r);break;case"horizontalLine":case"dashedHorizontalLine":k(x,u,[w.x,u.gridStart[1]],[r[0],u.options.y]);break;case"verticalLine":case"dashedVerticalLine":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;case"rectangle":k(x,u,[u.gridStart[0],w.y],[u.options.x,r[1]]);break;default:break}o=true;break}}}}}if(!o&&y.highlightObjectIndex!==null){q=y._tooltipElem;u=y.getObject(y.highlightObjectIndex);if(u.fadeTooltip){q.fadeOut(u.tooltipFadeSpeed)}else{q.hide()}y.highlightObjectIndex=null}}d.jqplot.postInitHooks.push(d.jqplot.CanvasOverlay.postPlotInit);d.jqplot.postDrawHooks.push(d.jqplot.CanvasOverlay.postPlotDraw);d.jqplot.eventListenerHooks.push(["jqplotMouseMove",a])})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.js b/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.js new file mode 100644 index 0000000..dc8434e --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.js @@ -0,0 +1,449 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + * included jsDate library by Chris Leonello: + * + * Copyright (c) 2010-2013 Chris Leonello + * + * jsDate is currently available for use in all personal or commercial projects + * under both the MIT and GPL version 2.0 licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * jsDate borrows many concepts and ideas from the Date Instance + * Methods by Ken Snyder along with some parts of Ken's actual code. + * + * Ken's original Date Instance Methods and copyright notice: + * + * Ken Snyder (ken d snyder at gmail dot com) + * 2008-09-10 + * version 2.0.2 (http://kendsnyder.com/sandbox/date/) + * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) + * + * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js. + * Larry has generously given permission to adapt his code for inclusion + * into jqPlot. + * + * Larry's original code can be found here: + * + * https://github.com/lsiden/export-jqplot-to-png + * + * + */ + +(function($) { + // This code is a modified version of the canvastext.js code, copyright below: + // + // This code is released to the public domain by Jim Studt, 2007. + // He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/ + // + $.jqplot.CanvasTextRenderer = function(options){ + this.fontStyle = 'normal'; // normal, italic, oblique [not implemented] + this.fontVariant = 'normal'; // normal, small caps [not implemented] + this.fontWeight = 'normal'; // normal, bold, bolder, lighter, 100 - 900 + this.fontSize = '10px'; + this.fontFamily = 'sans-serif'; + this.fontStretch = 1.0; + this.fillStyle = '#666666'; + this.angle = 0; + this.textAlign = 'start'; + this.textBaseline = 'alphabetic'; + this.text; + this.width; + this.height; + this.pt2px = 1.28; + + $.extend(true, this, options); + this.normalizedFontSize = this.normalizeFontSize(this.fontSize); + this.setHeight(); + }; + + $.jqplot.CanvasTextRenderer.prototype.init = function(options) { + $.extend(true, this, options); + this.normalizedFontSize = this.normalizeFontSize(this.fontSize); + this.setHeight(); + }; + + // convert css spec into point size + // returns float + $.jqplot.CanvasTextRenderer.prototype.normalizeFontSize = function(sz) { + sz = String(sz); + var n = parseFloat(sz); + if (sz.indexOf('px') > -1) { + return n/this.pt2px; + } + else if (sz.indexOf('pt') > -1) { + return n; + } + else if (sz.indexOf('em') > -1) { + return n*12; + } + else if (sz.indexOf('%') > -1) { + return n*12/100; + } + // default to pixels; + else { + return n/this.pt2px; + } + }; + + + $.jqplot.CanvasTextRenderer.prototype.fontWeight2Float = function(w) { + // w = normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 + // return values adjusted for Hershey font. + if (Number(w)) { + return w/400; + } + else { + switch (w) { + case 'normal': + return 1; + break; + case 'bold': + return 1.75; + break; + case 'bolder': + return 2.25; + break; + case 'lighter': + return 0.75; + break; + default: + return 1; + break; + } + } + }; + + $.jqplot.CanvasTextRenderer.prototype.getText = function() { + return this.text; + }; + + $.jqplot.CanvasTextRenderer.prototype.setText = function(t, ctx) { + this.text = t; + this.setWidth(ctx); + return this; + }; + + $.jqplot.CanvasTextRenderer.prototype.getWidth = function(ctx) { + return this.width; + }; + + $.jqplot.CanvasTextRenderer.prototype.setWidth = function(ctx, w) { + if (!w) { + this.width = this.measure(ctx, this.text); + } + else { + this.width = w; + } + return this; + }; + + // return height in pixels. + $.jqplot.CanvasTextRenderer.prototype.getHeight = function(ctx) { + return this.height; + }; + + // w - height in pt + // set heigh in px + $.jqplot.CanvasTextRenderer.prototype.setHeight = function(w) { + if (!w) { + //height = this.fontSize /0.75; + this.height = this.normalizedFontSize * this.pt2px; + } + else { + this.height = w; + } + return this; + }; + + $.jqplot.CanvasTextRenderer.prototype.letter = function (ch) + { + return this.letters[ch]; + }; + + $.jqplot.CanvasTextRenderer.prototype.ascent = function() + { + return this.normalizedFontSize; + }; + + $.jqplot.CanvasTextRenderer.prototype.descent = function() + { + return 7.0*this.normalizedFontSize/25.0; + }; + + $.jqplot.CanvasTextRenderer.prototype.measure = function(ctx, str) + { + var total = 0; + var len = str.length; + + for (var i = 0; i < len; i++) { + var c = this.letter(str.charAt(i)); + if (c) { + total += c.width * this.normalizedFontSize / 25.0 * this.fontStretch; + } + } + return total; + }; + + $.jqplot.CanvasTextRenderer.prototype.draw = function(ctx,str) + { + var x = 0; + // leave room at bottom for descenders. + var y = this.height*0.72; + var total = 0; + var len = str.length; + var mag = this.normalizedFontSize / 25.0; + + ctx.save(); + var tx, ty; + + // 1st quadrant + if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) { + tx = 0; + ty = -Math.sin(this.angle) * this.width; + } + // 4th quadrant + else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) { + tx = Math.sin(this.angle) * this.height; + ty = 0; + } + // 2nd quadrant + else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) { + tx = -Math.cos(this.angle) * this.width; + ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height; + } + // 3rd quadrant + else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) { + tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width; + ty = -Math.cos(this.angle) * this.height; + } + + ctx.strokeStyle = this.fillStyle; + ctx.fillStyle = this.fillStyle; + ctx.translate(tx, ty); + ctx.rotate(this.angle); + ctx.lineCap = "round"; + // multiplier was 2.0 + var fact = (this.normalizedFontSize > 30) ? 2.0 : 2 + (30 - this.normalizedFontSize)/20; + ctx.lineWidth = fact * mag * this.fontWeight2Float(this.fontWeight); + + for ( var i = 0; i < len; i++) { + var c = this.letter( str.charAt(i)); + if ( !c) { + continue; + } + + ctx.beginPath(); + + var penUp = 1; + var needStroke = 0; + for ( var j = 0; j < c.points.length; j++) { + var a = c.points[j]; + if ( a[0] == -1 && a[1] == -1) { + penUp = 1; + continue; + } + if ( penUp) { + ctx.moveTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag); + penUp = false; + } else { + ctx.lineTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag); + } + } + ctx.stroke(); + x += c.width*mag*this.fontStretch; + } + ctx.restore(); + return total; + }; + + $.jqplot.CanvasTextRenderer.prototype.letters = { + ' ': { width: 16, points: [] }, + '!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] }, + '#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] }, + '$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + '%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] }, + '&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] }, + '\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] }, + '(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] }, + ')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] }, + '*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] }, + '+': { width: 26, points: [[13,18],[13,0],[-1,-1],[4,9],[22,9]] }, + ',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '-': { width: 18, points: [[6,9],[12,9]] }, + '.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '/': { width: 22, points: [[20,25],[2,-7]] }, + '0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] }, + '1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] }, + '2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] }, + '3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] }, + '5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] }, + '7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] }, + '8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] }, + '9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] }, + ':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + ';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '<': { width: 24, points: [[20,18],[4,9],[20,0]] }, + '=': { width: 26, points: [[4,12],[22,12],[-1,-1],[4,6],[22,6]] }, + '>': { width: 24, points: [[4,18],[20,9],[4,0]] }, + '?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] }, + '@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] }, + 'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] }, + 'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] }, + 'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] }, + 'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] }, + 'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] }, + 'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] }, + 'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] }, + 'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] }, + 'I': { width: 8, points: [[4,21],[4,0]] }, + 'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] }, + 'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] }, + 'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] }, + 'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] }, + 'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] }, + 'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] }, + 'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] }, + 'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] }, + 'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] }, + 'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + 'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] }, + 'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] }, + 'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] }, + 'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] }, + 'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] }, + 'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] }, + 'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] }, + '[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] }, + '\\': { width: 14, points: [[0,21],[14,-3]] }, + ']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] }, + '^': { width: 16, points: [[6,15],[8,18],[10,15],[-1,-1],[3,12],[8,17],[13,12],[-1,-1],[8,17],[8,0]] }, + '_': { width: 16, points: [[0,-2],[16,-2]] }, + '`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] }, + 'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] }, + 'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] }, + 'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] }, + 'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] }, + 'l': { width: 8, points: [[4,21],[4,0]] }, + 'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] }, + 'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] }, + 'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] }, + 's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] }, + 't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] }, + 'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] }, + 'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] }, + 'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] }, + 'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] }, + 'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] }, + 'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] }, + '{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] }, + '|': { width: 8, points: [[4,25],[4,-7]] }, + '}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] }, + '~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] } + }; + + $.jqplot.CanvasFontRenderer = function(options) { + options = options || {}; + if (!options.pt2px) { + options.pt2px = 1.5; + } + $.jqplot.CanvasTextRenderer.call(this, options); + }; + + $.jqplot.CanvasFontRenderer.prototype = new $.jqplot.CanvasTextRenderer({}); + $.jqplot.CanvasFontRenderer.prototype.constructor = $.jqplot.CanvasFontRenderer; + + $.jqplot.CanvasFontRenderer.prototype.measure = function(ctx, str) + { + // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily; + var fstyle = this.fontSize+' '+this.fontFamily; + ctx.save(); + ctx.font = fstyle; + var w = ctx.measureText(str).width; + ctx.restore(); + return w; + }; + + $.jqplot.CanvasFontRenderer.prototype.draw = function(ctx, str) + { + var x = 0; + // leave room at bottom for descenders. + var y = this.height*0.72; + //var y = 12; + + ctx.save(); + var tx, ty; + + // 1st quadrant + if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) { + tx = 0; + ty = -Math.sin(this.angle) * this.width; + } + // 4th quadrant + else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) { + tx = Math.sin(this.angle) * this.height; + ty = 0; + } + // 2nd quadrant + else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) { + tx = -Math.cos(this.angle) * this.width; + ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height; + } + // 3rd quadrant + else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) { + tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width; + ty = -Math.cos(this.angle) * this.height; + } + ctx.strokeStyle = this.fillStyle; + ctx.fillStyle = this.fillStyle; + // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily; + var fstyle = this.fontSize+' '+this.fontFamily; + ctx.font = fstyle; + ctx.translate(tx, ty); + ctx.rotate(this.angle); + ctx.fillText(str, x, y); + // ctx.strokeText(str, x, y); + + ctx.restore(); + }; + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.min.js new file mode 100644 index 0000000..90199dd --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.canvasTextRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.CanvasTextRenderer=function(b){this.fontStyle="normal";this.fontVariant="normal";this.fontWeight="normal";this.fontSize="10px";this.fontFamily="sans-serif";this.fontStretch=1;this.fillStyle="#666666";this.angle=0;this.textAlign="start";this.textBaseline="alphabetic";this.text;this.width;this.height;this.pt2px=1.28;a.extend(true,this,b);this.normalizedFontSize=this.normalizeFontSize(this.fontSize);this.setHeight()};a.jqplot.CanvasTextRenderer.prototype.init=function(b){a.extend(true,this,b);this.normalizedFontSize=this.normalizeFontSize(this.fontSize);this.setHeight()};a.jqplot.CanvasTextRenderer.prototype.normalizeFontSize=function(b){b=String(b);var c=parseFloat(b);if(b.indexOf("px")>-1){return c/this.pt2px}else{if(b.indexOf("pt")>-1){return c}else{if(b.indexOf("em")>-1){return c*12}else{if(b.indexOf("%")>-1){return c*12/100}else{return c/this.pt2px}}}}};a.jqplot.CanvasTextRenderer.prototype.fontWeight2Float=function(b){if(Number(b)){return b/400}else{switch(b){case"normal":return 1;break;case"bold":return 1.75;break;case"bolder":return 2.25;break;case"lighter":return 0.75;break;default:return 1;break}}};a.jqplot.CanvasTextRenderer.prototype.getText=function(){return this.text};a.jqplot.CanvasTextRenderer.prototype.setText=function(c,b){this.text=c;this.setWidth(b);return this};a.jqplot.CanvasTextRenderer.prototype.getWidth=function(b){return this.width};a.jqplot.CanvasTextRenderer.prototype.setWidth=function(c,b){if(!b){this.width=this.measure(c,this.text)}else{this.width=b}return this};a.jqplot.CanvasTextRenderer.prototype.getHeight=function(b){return this.height};a.jqplot.CanvasTextRenderer.prototype.setHeight=function(b){if(!b){this.height=this.normalizedFontSize*this.pt2px}else{this.height=b}return this};a.jqplot.CanvasTextRenderer.prototype.letter=function(b){return this.letters[b]};a.jqplot.CanvasTextRenderer.prototype.ascent=function(){return this.normalizedFontSize};a.jqplot.CanvasTextRenderer.prototype.descent=function(){return 7*this.normalizedFontSize/25};a.jqplot.CanvasTextRenderer.prototype.measure=function(d,g){var f=0;var b=g.length;for(var e=0;e<b;e++){var h=this.letter(g.charAt(e));if(h){f+=h.width*this.normalizedFontSize/25*this.fontStretch}}return f};a.jqplot.CanvasTextRenderer.prototype.draw=function(s,n){var r=0;var o=this.height*0.72;var p=0;var l=n.length;var k=this.normalizedFontSize/25;s.save();var h,f;if((-Math.PI/2<=this.angle&&this.angle<=0)||(Math.PI*3/2<=this.angle&&this.angle<=Math.PI*2)){h=0;f=-Math.sin(this.angle)*this.width}else{if((0<this.angle&&this.angle<=Math.PI/2)||(-Math.PI*2<=this.angle&&this.angle<=-Math.PI*3/2)){h=Math.sin(this.angle)*this.height;f=0}else{if((-Math.PI<this.angle&&this.angle<-Math.PI/2)||(Math.PI<=this.angle&&this.angle<=Math.PI*3/2)){h=-Math.cos(this.angle)*this.width;f=-Math.sin(this.angle)*this.width-Math.cos(this.angle)*this.height}else{if((-Math.PI*3/2<this.angle&&this.angle<Math.PI)||(Math.PI/2<this.angle&&this.angle<Math.PI)){h=Math.sin(this.angle)*this.height-Math.cos(this.angle)*this.width;f=-Math.cos(this.angle)*this.height}}}}s.strokeStyle=this.fillStyle;s.fillStyle=this.fillStyle;s.translate(h,f);s.rotate(this.angle);s.lineCap="round";var t=(this.normalizedFontSize>30)?2:2+(30-this.normalizedFontSize)/20;s.lineWidth=t*k*this.fontWeight2Float(this.fontWeight);for(var g=0;g<l;g++){var m=this.letter(n.charAt(g));if(!m){continue}s.beginPath();var e=1;var b=0;for(var d=0;d<m.points.length;d++){var q=m.points[d];if(q[0]==-1&&q[1]==-1){e=1;continue}if(e){s.moveTo(r+q[0]*k*this.fontStretch,o-q[1]*k);e=false}else{s.lineTo(r+q[0]*k*this.fontStretch,o-q[1]*k)}}s.stroke();r+=m.width*k*this.fontStretch}s.restore();return p};a.jqplot.CanvasTextRenderer.prototype.letters={" ":{width:16,points:[]},"!":{width:10,points:[[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]]},'"':{width:16,points:[[4,21],[4,14],[-1,-1],[12,21],[12,14]]},"#":{width:21,points:[[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]]},"$":{width:20,points:[[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]]},"%":{width:24,points:[[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]]},"&":{width:26,points:[[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]]},"'":{width:10,points:[[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]]},"(":{width:14,points:[[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]]},")":{width:14,points:[[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]]},"*":{width:16,points:[[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]]},"+":{width:26,points:[[13,18],[13,0],[-1,-1],[4,9],[22,9]]},",":{width:10,points:[[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]]},"-":{width:18,points:[[6,9],[12,9]]},".":{width:10,points:[[5,2],[4,1],[5,0],[6,1],[5,2]]},"/":{width:22,points:[[20,25],[2,-7]]},"0":{width:20,points:[[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]]},"1":{width:20,points:[[6,17],[8,18],[11,21],[11,0]]},"2":{width:20,points:[[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]]},"3":{width:20,points:[[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]]},"4":{width:20,points:[[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]]},"5":{width:20,points:[[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]]},"6":{width:20,points:[[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]]},"7":{width:20,points:[[17,21],[7,0],[-1,-1],[3,21],[17,21]]},"8":{width:20,points:[[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]]},"9":{width:20,points:[[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]]},":":{width:10,points:[[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]]},";":{width:10,points:[[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]]},"<":{width:24,points:[[20,18],[4,9],[20,0]]},"=":{width:26,points:[[4,12],[22,12],[-1,-1],[4,6],[22,6]]},">":{width:24,points:[[4,18],[20,9],[4,0]]},"?":{width:18,points:[[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]]},"@":{width:27,points:[[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]]},A:{width:18,points:[[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]]},B:{width:21,points:[[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]]},C:{width:21,points:[[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]]},D:{width:21,points:[[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]]},E:{width:19,points:[[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]]},F:{width:18,points:[[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]]},G:{width:21,points:[[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]]},H:{width:22,points:[[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]]},I:{width:8,points:[[4,21],[4,0]]},J:{width:16,points:[[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]]},K:{width:21,points:[[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]]},L:{width:17,points:[[4,21],[4,0],[-1,-1],[4,0],[16,0]]},M:{width:24,points:[[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]]},N:{width:22,points:[[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]]},O:{width:22,points:[[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]]},P:{width:21,points:[[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]]},Q:{width:22,points:[[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]]},R:{width:21,points:[[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]]},S:{width:20,points:[[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]]},T:{width:16,points:[[8,21],[8,0],[-1,-1],[1,21],[15,21]]},U:{width:22,points:[[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]]},V:{width:18,points:[[1,21],[9,0],[-1,-1],[17,21],[9,0]]},W:{width:24,points:[[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]]},X:{width:20,points:[[3,21],[17,0],[-1,-1],[17,21],[3,0]]},Y:{width:18,points:[[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]]},Z:{width:20,points:[[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]]},"[":{width:14,points:[[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]]},"\\":{width:14,points:[[0,21],[14,-3]]},"]":{width:14,points:[[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]]},"^":{width:16,points:[[6,15],[8,18],[10,15],[-1,-1],[3,12],[8,17],[13,12],[-1,-1],[8,17],[8,0]]},_:{width:16,points:[[0,-2],[16,-2]]},"`":{width:10,points:[[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]]},a:{width:19,points:[[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},b:{width:19,points:[[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]]},c:{width:18,points:[[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},d:{width:19,points:[[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},e:{width:18,points:[[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},f:{width:12,points:[[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]]},g:{width:19,points:[[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},h:{width:19,points:[[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]]},i:{width:8,points:[[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]]},j:{width:10,points:[[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]]},k:{width:17,points:[[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]]},l:{width:8,points:[[4,21],[4,0]]},m:{width:30,points:[[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]]},n:{width:19,points:[[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]]},o:{width:19,points:[[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]]},p:{width:19,points:[[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]]},q:{width:19,points:[[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]]},r:{width:13,points:[[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]]},s:{width:17,points:[[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]]},t:{width:12,points:[[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]]},u:{width:19,points:[[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]]},v:{width:16,points:[[2,14],[8,0],[-1,-1],[14,14],[8,0]]},w:{width:22,points:[[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]]},x:{width:17,points:[[3,14],[14,0],[-1,-1],[14,14],[3,0]]},y:{width:16,points:[[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]]},z:{width:17,points:[[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]]},"{":{width:14,points:[[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]]},"|":{width:8,points:[[4,25],[4,-7]]},"}":{width:14,points:[[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]]},"~":{width:24,points:[[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]]}};a.jqplot.CanvasFontRenderer=function(b){b=b||{};if(!b.pt2px){b.pt2px=1.5}a.jqplot.CanvasTextRenderer.call(this,b)};a.jqplot.CanvasFontRenderer.prototype=new a.jqplot.CanvasTextRenderer({});a.jqplot.CanvasFontRenderer.prototype.constructor=a.jqplot.CanvasFontRenderer;a.jqplot.CanvasFontRenderer.prototype.measure=function(c,e){var d=this.fontSize+" "+this.fontFamily;c.save();c.font=d;var b=c.measureText(e).width;c.restore();return b};a.jqplot.CanvasFontRenderer.prototype.draw=function(e,g){var c=0;var h=this.height*0.72;e.save();var d,b;if((-Math.PI/2<=this.angle&&this.angle<=0)||(Math.PI*3/2<=this.angle&&this.angle<=Math.PI*2)){d=0;b=-Math.sin(this.angle)*this.width}else{if((0<this.angle&&this.angle<=Math.PI/2)||(-Math.PI*2<=this.angle&&this.angle<=-Math.PI*3/2)){d=Math.sin(this.angle)*this.height;b=0}else{if((-Math.PI<this.angle&&this.angle<-Math.PI/2)||(Math.PI<=this.angle&&this.angle<=Math.PI*3/2)){d=-Math.cos(this.angle)*this.width;b=-Math.sin(this.angle)*this.width-Math.cos(this.angle)*this.height}else{if((-Math.PI*3/2<this.angle&&this.angle<Math.PI)||(Math.PI/2<this.angle&&this.angle<Math.PI)){d=Math.sin(this.angle)*this.height-Math.cos(this.angle)*this.width;b=-Math.cos(this.angle)*this.height}}}}e.strokeStyle=this.fillStyle;e.fillStyle=this.fillStyle;var f=this.fontSize+" "+this.fontFamily;e.font=f;e.translate(d,b);e.rotate(this.angle);e.fillText(g,c,h);e.restore()}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.js b/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.js new file mode 100644 index 0000000..aa77120 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.js @@ -0,0 +1,679 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * class: $.jqplot.CategoryAxisRenderer + * A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series. + * + * To use this renderer, include the plugin in your source + * > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script> + * + * and supply the appropriate options to your plot + * + * > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}} + **/ + $.jqplot.CategoryAxisRenderer = function(options) { + $.jqplot.LinearAxisRenderer.call(this); + // prop: sortMergedLabels + // True to sort tick labels when labels are created by merging + // x axis values from multiple series. That is, say you have + // two series like: + // > line1 = [[2006, 4], [2008, 9], [2009, 16]]; + // > line2 = [[2006, 3], [2007, 7], [2008, 6]]; + // If no label array is specified, tick labels will be collected + // from the x values of the series. With sortMergedLabels + // set to true, tick labels will be: + // > [2006, 2007, 2008, 2009] + // With sortMergedLabels set to false, tick labels will be: + // > [2006, 2008, 2009, 2007] + // + // Note, this property is specified on the renderOptions for the + // axes when creating a plot: + // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}} + this.sortMergedLabels = false; + }; + + $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer; + + $.jqplot.CategoryAxisRenderer.prototype.init = function(options){ + this.groups = 1; + this.groupLabels = []; + this._groupLabels = []; + this._grouped = false; + this._barsPerGroup = null; + this.reverse = false; + // prop: tickRenderer + // A class of a rendering engine for creating the ticks labels displayed on the plot, + // See <$.jqplot.AxisTickRenderer>. + // this.tickRenderer = $.jqplot.AxisTickRenderer; + // this.labelRenderer = $.jqplot.AxisLabelRenderer; + $.extend(true, this, {tickOptions:{formatString:'%d'}}, options); + var db = this._dataBounds; + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + if (s.groups) { + this.groups = s.groups; + } + var d = s.data; + + for (var j=0; j<d.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + if (d[j][0] < db.min || db.min == null) { + db.min = d[j][0]; + } + if (d[j][0] > db.max || db.max == null) { + db.max = d[j][0]; + } + } + else { + if (d[j][1] < db.min || db.min == null) { + db.min = d[j][1]; + } + if (d[j][1] > db.max || db.max == null) { + db.max = d[j][1]; + } + } + } + } + + if (this.groupLabels.length) { + this.groups = this.groupLabels.length; + } + }; + + + $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() { + // we're are operating on an axis here + var ticks = this._ticks; + var userTicks = this.ticks; + var name = this.name; + // databounds were set on axis initialization. + var db = this._dataBounds; + var dim, interval; + var min, max; + var pos1, pos2; + var tt, i; + + // if we already have ticks, use them. + if (userTicks.length) { + // adjust with blanks if we have groups + if (this.groups > 1 && !this._grouped) { + var l = userTicks.length; + var skip = parseInt(l/this.groups, 10); + var count = 0; + for (var i=skip; i<l; i+=skip) { + userTicks.splice(i+count, 0, ' '); + count++; + } + this._grouped = true; + } + this.min = 0.5; + this.max = userTicks.length + 0.5; + var range = this.max - this.min; + this.numberTicks = 2*userTicks.length + 1; + for (i=0; i<userTicks.length; i++){ + tt = this.min + 2 * i * range / (this.numberTicks-1); + // need a marker before and after the tick + var t = new this.tickRenderer(this.tickOptions); + t.showLabel = false; + // t.showMark = true; + t.setTick(tt, this.name); + this._ticks.push(t); + var t = new this.tickRenderer(this.tickOptions); + t.label = userTicks[i]; + // t.showLabel = true; + t.showMark = false; + t.showGridline = false; + t.setTick(tt+0.5, this.name); + this._ticks.push(t); + } + // now add the last tick at the end + var t = new this.tickRenderer(this.tickOptions); + t.showLabel = false; + // t.showMark = true; + t.setTick(tt+1, this.name); + this._ticks.push(t); + } + + // we don't have any ticks yet, let's make some! + else { + if (name == 'xaxis' || name == 'x2axis') { + dim = this._plotDimensions.width; + } + else { + dim = this._plotDimensions.height; + } + + // if min, max and number of ticks specified, user can't specify interval. + if (this.min != null && this.max != null && this.numberTicks != null) { + this.tickInterval = null; + } + + // if max, min, and interval specified and interval won't fit, ignore interval. + if (this.min != null && this.max != null && this.tickInterval != null) { + if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) { + this.tickInterval = null; + } + } + + // find out how many categories are in the lines and collect labels + var labels = []; + var numcats = 0; + var min = 0.5; + var max, val; + var isMerged = false; + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + for (var j=0; j<s.data.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + val = s.data[j][0]; + } + else { + val = s.data[j][1]; + } + if ($.inArray(val, labels) == -1) { + isMerged = true; + numcats += 1; + labels.push(val); + } + } + } + + if (isMerged && this.sortMergedLabels) { + if (typeof labels[0] == "string") { + labels.sort(); + } else { + labels.sort(function(a,b) { return a - b; }); + } + } + + // keep a reference to these tick labels to use for redrawing plot (see bug #57) + this.ticks = labels; + + // now bin the data values to the right lables. + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + for (var j=0; j<s.data.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + val = s.data[j][0]; + } + else { + val = s.data[j][1]; + } + // for category axis, force the values into category bins. + // we should have the value in the label array now. + var idx = $.inArray(val, labels)+1; + if (this.name == 'xaxis' || this.name == 'x2axis') { + s.data[j][0] = idx; + } + else { + s.data[j][1] = idx; + } + } + } + + // adjust with blanks if we have groups + if (this.groups > 1 && !this._grouped) { + var l = labels.length; + var skip = parseInt(l/this.groups, 10); + var count = 0; + for (var i=skip; i<l; i+=skip+1) { + labels[i] = ' '; + } + this._grouped = true; + } + + max = numcats + 0.5; + if (this.numberTicks == null) { + this.numberTicks = 2*numcats + 1; + } + + var range = max - min; + this.min = min; + this.max = max; + var track = 0; + + // todo: adjust this so more ticks displayed. + var maxVisibleTicks = parseInt(3+dim/10, 10); + var skip = parseInt(numcats/maxVisibleTicks, 10); + + if (this.tickInterval == null) { + + this.tickInterval = range / (this.numberTicks-1); + + } + // if tickInterval is specified, we will ignore any computed maximum. + for (var i=0; i<this.numberTicks; i++){ + tt = this.min + i * this.tickInterval; + var t = new this.tickRenderer(this.tickOptions); + // if even tick, it isn't a category, it's a divider + if (i/2 == parseInt(i/2, 10)) { + t.showLabel = false; + t.showMark = true; + } + else { + if (skip>0 && track<skip) { + t.showLabel = false; + track += 1; + } + else { + t.showLabel = true; + track = 0; + } + t.label = t.formatter(t.formatString, labels[(i-1)/2]); + t.showMark = false; + t.showGridline = false; + } + t.setTick(tt, this.name); + this._ticks.push(t); + } + } + + }; + + // called with scope of axis + $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) { + if (this.show) { + // populate the axis label and value properties. + // createTicks is a method on the renderer, but + // call it within the scope of the axis. + this.renderer.createTicks.call(this); + // fill a div with axes labels in the right direction. + // Need to pregenerate each axis to get its bounds and + // position it and the labels correctly on the plot. + var dim=0; + var temp; + // Added for theming. + if (this._elem) { + // this._elem.empty(); + // Memory Leaks patch + this._elem.emptyForce(); + } + + this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>'); + + if (this.name == 'xaxis' || this.name == 'x2axis') { + this._elem.width(this._plotDimensions.width); + } + else { + this._elem.height(this._plotDimensions.height); + } + + // create a _label object. + this.labelOptions.axis = this.name; + this._label = new this.labelRenderer(this.labelOptions); + if (this._label.show) { + var elem = this._label.draw(ctx, plot); + elem.appendTo(this._elem); + } + + var t = this._ticks; + for (var i=0; i<t.length; i++) { + var tick = t[i]; + if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + var elem = tick.draw(ctx, plot); + elem.appendTo(this._elem); + } + } + + this._groupLabels = []; + // now make group labels + for (var i=0; i<this.groupLabels.length; i++) + { + var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>'); + elem.html(this.groupLabels[i]); + this._groupLabels.push(elem); + elem.appendTo(this._elem); + } + } + return this._elem; + }; + + // called with scope of axis + $.jqplot.CategoryAxisRenderer.prototype.set = function() { + var dim = 0; + var temp; + var w = 0; + var h = 0; + var lshow = (this._label == null) ? false : this._label.show; + if (this.show) { + var t = this._ticks; + for (var i=0; i<t.length; i++) { + var tick = t[i]; + if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + temp = tick._elem.outerHeight(true); + } + else { + temp = tick._elem.outerWidth(true); + } + if (temp > dim) { + dim = temp; + } + } + } + + var dim2 = 0; + for (var i=0; i<this._groupLabels.length; i++) { + var l = this._groupLabels[i]; + if (this.name == 'xaxis' || this.name == 'x2axis') { + temp = l.outerHeight(true); + } + else { + temp = l.outerWidth(true); + } + if (temp > dim2) { + dim2 = temp; + } + } + + if (lshow) { + w = this._label._elem.outerWidth(true); + h = this._label._elem.outerHeight(true); + } + if (this.name == 'xaxis') { + dim += dim2 + h; + this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); + } + else if (this.name == 'x2axis') { + dim += dim2 + h; + this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); + } + else if (this.name == 'yaxis') { + dim += dim2 + w; + this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + else { + dim += dim2 + w; + this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + } + }; + + // called with scope of axis + $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) { + var ticks = this._ticks; + var max = this.max; + var min = this.min; + var offmax = offsets.max; + var offmin = offsets.min; + var lshow = (this._label == null) ? false : this._label.show; + var i; + + for (var p in pos) { + this._elem.css(p, pos[p]); + } + + this._offsets = offsets; + // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. + var pixellength = offmax - offmin; + var unitlength = max - min; + + if (!this.reverse) { + // point to unit and unit to point conversions references to Plot DOM element top left corner. + + this.u2p = function(u){ + return (u - min) * pixellength / unitlength + offmin; + }; + + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + if (this.name == 'xaxis' || this.name == 'x2axis'){ + this.series_u2p = function(u){ + return (u - min) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + else { + this.series_u2p = function(u){ + return (u - max) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + } + + else { + // point to unit and unit to point conversions references to Plot DOM element top left corner. + + this.u2p = function(u){ + return offmin + (max - u) * pixellength / unitlength; + }; + + this.p2u = function(p){ + return min + (p - offmin) * unitlength / pixellength; + }; + + if (this.name == 'xaxis' || this.name == 'x2axis'){ + this.series_u2p = function(u){ + return (max - u) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + + else { + this.series_u2p = function(u){ + return (min - u) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + } + + + if (this.show) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + for (i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'xaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + if (temp * t.angle < 0) { + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + } + // position at start + else { + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + } + break; + case 'end': + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + case 'start': + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + break; + case 'middle': + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + default: + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + } + } + else { + shim = -t.getWidth()/2; + } + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('left', val); + t.pack(); + } + } + + var labeledge=['bottom', 0]; + if (lshow) { + var w = this._label._elem.outerWidth(true); + this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); + if (this.name == 'xaxis') { + this._label._elem.css('bottom', '0px'); + labeledge = ['bottom', this._label._elem.outerHeight(true)]; + } + else { + this._label._elem.css('top', '0px'); + labeledge = ['top', this._label._elem.outerHeight(true)]; + } + this._label.pack(); + } + + // draw the group labels + var step = parseInt(this._ticks.length/this.groups, 10) + 1; + for (i=0; i<this._groupLabels.length; i++) { + var mid = 0; + var count = 0; + for (var j=i*step; j<(i+1)*step; j++) { + if (j >= this._ticks.length-1) continue; // the last tick does not exist as there is no other group in order to have an empty one. + if (this._ticks[j]._elem && this._ticks[j].label != " ") { + var t = this._ticks[j]._elem; + var p = t.position(); + mid += p.left + t.outerWidth(true)/2; + count++; + } + } + mid = mid/count; + this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)}); + this._groupLabels[i].css(labeledge[0], labeledge[1]); + } + } + else { + for (i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'yaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + case 'end': + if (temp * t.angle < 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'start': + if (t.angle > 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'middle': + // if (t.angle > 0) { + // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + // } + // else { + // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + // } + shim = -t.getHeight()/2; + break; + default: + shim = -t.getHeight()/2; + break; + } + } + else { + shim = -t.getHeight()/2; + } + + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('top', val); + t.pack(); + } + } + + var labeledge=['left', 0]; + if (lshow) { + var h = this._label._elem.outerHeight(true); + this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); + if (this.name == 'yaxis') { + this._label._elem.css('left', '0px'); + labeledge = ['left', this._label._elem.outerWidth(true)]; + } + else { + this._label._elem.css('right', '0px'); + labeledge = ['right', this._label._elem.outerWidth(true)]; + } + this._label.pack(); + } + + // draw the group labels, position top here, do left after label position. + var step = parseInt(this._ticks.length/this.groups, 10) + 1; // step is one more than before as we don't want to have overlaps in loops + for (i=0; i<this._groupLabels.length; i++) { + var mid = 0; + var count = 0; + for (var j=i*step; j<(i+1)*step; j++) { // j must never reach (i+1)*step as we don't want to have overlap between loops + if (j >= this._ticks.length-1) continue; // the last tick does not exist as there is no other group in order to have an empty one. + if (this._ticks[j]._elem && this._ticks[j].label != " ") { + var t = this._ticks[j]._elem; + var p = t.position(); + mid += p.top + t.outerHeight()/2; + count++; + } + } + mid = mid/count; + this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2}); + this._groupLabels[i].css(labeledge[0], labeledge[1]); + + } + } + } + }; + + +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.min.js new file mode 100644 index 0000000..db61f92 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.categoryAxisRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.CategoryAxisRenderer=function(b){a.jqplot.LinearAxisRenderer.call(this);this.sortMergedLabels=false};a.jqplot.CategoryAxisRenderer.prototype=new a.jqplot.LinearAxisRenderer();a.jqplot.CategoryAxisRenderer.prototype.constructor=a.jqplot.CategoryAxisRenderer;a.jqplot.CategoryAxisRenderer.prototype.init=function(e){this.groups=1;this.groupLabels=[];this._groupLabels=[];this._grouped=false;this._barsPerGroup=null;this.reverse=false;a.extend(true,this,{tickOptions:{formatString:"%d"}},e);var b=this._dataBounds;for(var f=0;f<this._series.length;f++){var g=this._series[f];if(g.groups){this.groups=g.groups}var h=g.data;for(var c=0;c<h.length;c++){if(this.name=="xaxis"||this.name=="x2axis"){if(h[c][0]<b.min||b.min==null){b.min=h[c][0]}if(h[c][0]>b.max||b.max==null){b.max=h[c][0]}}else{if(h[c][1]<b.min||b.min==null){b.min=h[c][1]}if(h[c][1]>b.max||b.max==null){b.max=h[c][1]}}}}if(this.groupLabels.length){this.groups=this.groupLabels.length}};a.jqplot.CategoryAxisRenderer.prototype.createTicks=function(){var D=this._ticks;var z=this.ticks;var F=this.name;var C=this._dataBounds;var v,A;var q,w;var d,c;var b,x;if(z.length){if(this.groups>1&&!this._grouped){var r=z.length;var p=parseInt(r/this.groups,10);var e=0;for(var x=p;x<r;x+=p){z.splice(x+e,0," ");e++}this._grouped=true}this.min=0.5;this.max=z.length+0.5;var m=this.max-this.min;this.numberTicks=2*z.length+1;for(x=0;x<z.length;x++){b=this.min+2*x*m/(this.numberTicks-1);var h=new this.tickRenderer(this.tickOptions);h.showLabel=false;h.setTick(b,this.name);this._ticks.push(h);var h=new this.tickRenderer(this.tickOptions);h.label=z[x];h.showMark=false;h.showGridline=false;h.setTick(b+0.5,this.name);this._ticks.push(h)}var h=new this.tickRenderer(this.tickOptions);h.showLabel=false;h.setTick(b+1,this.name);this._ticks.push(h)}else{if(F=="xaxis"||F=="x2axis"){v=this._plotDimensions.width}else{v=this._plotDimensions.height}if(this.min!=null&&this.max!=null&&this.numberTicks!=null){this.tickInterval=null}if(this.min!=null&&this.max!=null&&this.tickInterval!=null){if(parseInt((this.max-this.min)/this.tickInterval,10)!=(this.max-this.min)/this.tickInterval){this.tickInterval=null}}var y=[];var B=0;var q=0.5;var w,E;var f=false;for(var x=0;x<this._series.length;x++){var k=this._series[x];for(var u=0;u<k.data.length;u++){if(this.name=="xaxis"||this.name=="x2axis"){E=k.data[u][0]}else{E=k.data[u][1]}if(a.inArray(E,y)==-1){f=true;B+=1;y.push(E)}}}if(f&&this.sortMergedLabels){if(typeof y[0]=="string"){y.sort()}else{y.sort(function(j,i){return j-i})}}this.ticks=y;for(var x=0;x<this._series.length;x++){var k=this._series[x];for(var u=0;u<k.data.length;u++){if(this.name=="xaxis"||this.name=="x2axis"){E=k.data[u][0]}else{E=k.data[u][1]}var n=a.inArray(E,y)+1;if(this.name=="xaxis"||this.name=="x2axis"){k.data[u][0]=n}else{k.data[u][1]=n}}}if(this.groups>1&&!this._grouped){var r=y.length;var p=parseInt(r/this.groups,10);var e=0;for(var x=p;x<r;x+=p+1){y[x]=" "}this._grouped=true}w=B+0.5;if(this.numberTicks==null){this.numberTicks=2*B+1}var m=w-q;this.min=q;this.max=w;var o=0;var g=parseInt(3+v/10,10);var p=parseInt(B/g,10);if(this.tickInterval==null){this.tickInterval=m/(this.numberTicks-1)}for(var x=0;x<this.numberTicks;x++){b=this.min+x*this.tickInterval;var h=new this.tickRenderer(this.tickOptions);if(x/2==parseInt(x/2,10)){h.showLabel=false;h.showMark=true}else{if(p>0&&o<p){h.showLabel=false;o+=1}else{h.showLabel=true;o=0}h.label=h.formatter(h.formatString,y[(x-1)/2]);h.showMark=false;h.showGridline=false}h.setTick(b,this.name);this._ticks.push(h)}}};a.jqplot.CategoryAxisRenderer.prototype.draw=function(b,j){if(this.show){this.renderer.createTicks.call(this);var h=0;var c;if(this._elem){this._elem.emptyForce()}this._elem=this._elem||a('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');if(this.name=="xaxis"||this.name=="x2axis"){this._elem.width(this._plotDimensions.width)}else{this._elem.height(this._plotDimensions.height)}this.labelOptions.axis=this.name;this._label=new this.labelRenderer(this.labelOptions);if(this._label.show){var g=this._label.draw(b,j);g.appendTo(this._elem)}var f=this._ticks;for(var e=0;e<f.length;e++){var d=f[e];if(d.showLabel&&(!d.isMinorTick||this.showMinorTicks)){var g=d.draw(b,j);g.appendTo(this._elem)}}this._groupLabels=[];for(var e=0;e<this.groupLabels.length;e++){var g=a('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>');g.html(this.groupLabels[e]);this._groupLabels.push(g);g.appendTo(this._elem)}}return this._elem};a.jqplot.CategoryAxisRenderer.prototype.set=function(){var e=0;var m;var k=0;var f=0;var d=(this._label==null)?false:this._label.show;if(this.show){var n=this._ticks;for(var c=0;c<n.length;c++){var g=n[c];if(g.showLabel&&(!g.isMinorTick||this.showMinorTicks)){if(this.name=="xaxis"||this.name=="x2axis"){m=g._elem.outerHeight(true)}else{m=g._elem.outerWidth(true)}if(m>e){e=m}}}var j=0;for(var c=0;c<this._groupLabels.length;c++){var b=this._groupLabels[c];if(this.name=="xaxis"||this.name=="x2axis"){m=b.outerHeight(true)}else{m=b.outerWidth(true)}if(m>j){j=m}}if(d){k=this._label._elem.outerWidth(true);f=this._label._elem.outerHeight(true)}if(this.name=="xaxis"){e+=j+f;this._elem.css({height:e+"px",left:"0px",bottom:"0px"})}else{if(this.name=="x2axis"){e+=j+f;this._elem.css({height:e+"px",left:"0px",top:"0px"})}else{if(this.name=="yaxis"){e+=j+k;this._elem.css({width:e+"px",left:"0px",top:"0px"});if(d&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",k+"px")}}else{e+=j+k;this._elem.css({width:e+"px",right:"0px",top:"0px"});if(d&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",k+"px")}}}}}};a.jqplot.CategoryAxisRenderer.prototype.pack=function(e,c){var C=this._ticks;var v=this.max;var s=this.min;var n=c.max;var l=c.min;var q=(this._label==null)?false:this._label.show;var x;for(var r in e){this._elem.css(r,e[r])}this._offsets=c;var g=n-l;var k=v-s;if(!this.reverse){this.u2p=function(h){return(h-s)*g/k+l};this.p2u=function(h){return(h-l)*k/g+s};if(this.name=="xaxis"||this.name=="x2axis"){this.series_u2p=function(h){return(h-s)*g/k};this.series_p2u=function(h){return h*k/g+s}}else{this.series_u2p=function(h){return(h-v)*g/k};this.series_p2u=function(h){return h*k/g+v}}}else{this.u2p=function(h){return l+(v-h)*g/k};this.p2u=function(h){return s+(h-l)*k/g};if(this.name=="xaxis"||this.name=="x2axis"){this.series_u2p=function(h){return(v-h)*g/k};this.series_p2u=function(h){return h*k/g+v}}else{this.series_u2p=function(h){return(s-h)*g/k};this.series_p2u=function(h){return h*k/g+s}}}if(this.show){if(this.name=="xaxis"||this.name=="x2axis"){for(x=0;x<C.length;x++){var o=C[x];if(o.show&&o.showLabel){var b;if(o.constructor==a.jqplot.CanvasAxisTickRenderer&&o.angle){var A=(this.name=="xaxis")?1:-1;switch(o.labelPosition){case"auto":if(A*o.angle<0){b=-o.getWidth()+o._textRenderer.height*Math.sin(-o._textRenderer.angle)/2}else{b=-o._textRenderer.height*Math.sin(o._textRenderer.angle)/2}break;case"end":b=-o.getWidth()+o._textRenderer.height*Math.sin(-o._textRenderer.angle)/2;break;case"start":b=-o._textRenderer.height*Math.sin(o._textRenderer.angle)/2;break;case"middle":b=-o.getWidth()/2+o._textRenderer.height*Math.sin(-o._textRenderer.angle)/2;break;default:b=-o.getWidth()/2+o._textRenderer.height*Math.sin(-o._textRenderer.angle)/2;break}}else{b=-o.getWidth()/2}var D=this.u2p(o.value)+b+"px";o._elem.css("left",D);o.pack()}}var z=["bottom",0];if(q){var m=this._label._elem.outerWidth(true);this._label._elem.css("left",l+g/2-m/2+"px");if(this.name=="xaxis"){this._label._elem.css("bottom","0px");z=["bottom",this._label._elem.outerHeight(true)]}else{this._label._elem.css("top","0px");z=["top",this._label._elem.outerHeight(true)]}this._label.pack()}var d=parseInt(this._ticks.length/this.groups,10)+1;for(x=0;x<this._groupLabels.length;x++){var B=0;var f=0;for(var u=x*d;u<(x+1)*d;u++){if(u>=this._ticks.length-1){continue}if(this._ticks[u]._elem&&this._ticks[u].label!=" "){var o=this._ticks[u]._elem;var r=o.position();B+=r.left+o.outerWidth(true)/2;f++}}B=B/f;this._groupLabels[x].css({left:(B-this._groupLabels[x].outerWidth(true)/2)});this._groupLabels[x].css(z[0],z[1])}}else{for(x=0;x<C.length;x++){var o=C[x];if(o.show&&o.showLabel){var b;if(o.constructor==a.jqplot.CanvasAxisTickRenderer&&o.angle){var A=(this.name=="yaxis")?1:-1;switch(o.labelPosition){case"auto":case"end":if(A*o.angle<0){b=-o._textRenderer.height*Math.cos(-o._textRenderer.angle)/2}else{b=-o.getHeight()+o._textRenderer.height*Math.cos(o._textRenderer.angle)/2}break;case"start":if(o.angle>0){b=-o._textRenderer.height*Math.cos(-o._textRenderer.angle)/2}else{b=-o.getHeight()+o._textRenderer.height*Math.cos(o._textRenderer.angle)/2}break;case"middle":b=-o.getHeight()/2;break;default:b=-o.getHeight()/2;break}}else{b=-o.getHeight()/2}var D=this.u2p(o.value)+b+"px";o._elem.css("top",D);o.pack()}}var z=["left",0];if(q){var y=this._label._elem.outerHeight(true);this._label._elem.css("top",n-g/2-y/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px");z=["left",this._label._elem.outerWidth(true)]}else{this._label._elem.css("right","0px");z=["right",this._label._elem.outerWidth(true)]}this._label.pack()}var d=parseInt(this._ticks.length/this.groups,10)+1;for(x=0;x<this._groupLabels.length;x++){var B=0;var f=0;for(var u=x*d;u<(x+1)*d;u++){if(u>=this._ticks.length-1){continue}if(this._ticks[u]._elem&&this._ticks[u].label!=" "){var o=this._ticks[u]._elem;var r=o.position();B+=r.top+o.outerHeight()/2;f++}}B=B/f;this._groupLabels[x].css({top:B-this._groupLabels[x].outerHeight()/2});this._groupLabels[x].css(z[0],z[1])}}}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.ciParser.js b/web/extensions/jqplot/plugins/jqplot.ciParser.js new file mode 100644 index 0000000..2f094fc --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.ciParser.js @@ -0,0 +1,116 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.ciParser + * Data Renderer function which converts a custom JSON data object into jqPlot data format. + * Set this as a callable on the jqplot dataRenderer plot option: + * + * > plot = $.jqplot('mychart', [data], { dataRenderer: $.jqplot.ciParser, ... }); + * + * Where data is an object in JSON format or a JSON encoded string conforming to the + * City Index API spec. + * + * Note that calling the renderer function is handled internally by jqPlot. The + * user does not have to call the function. The parameters described below will + * automatically be passed to the ciParser function. + * + * Parameters: + * data - JSON encoded string or object. + * plot - reference to jqPlot Plot object. + * + * Returns: + * data array in jqPlot format. + * + */ + $.jqplot.ciParser = function (data, plot) { + var ret = [], + line, + temp, + i, j, k, kk; + + if (typeof(data) == "string") { + data = $.jqplot.JSON.parse(data, handleStrings); + } + + else if (typeof(data) == "object") { + for (k in data) { + for (i=0; i<data[k].length; i++) { + for (kk in data[k][i]) { + data[k][i][kk] = handleStrings(kk, data[k][i][kk]); + } + } + } + } + + else { + return null; + } + + // function handleStrings + // Checks any JSON encoded strings to see if they are + // encoded dates. If so, pull out the timestamp. + // Expects dates to be represented by js timestamps. + + function handleStrings(key, value) { + var a; + if (value != null) { + if (value.toString().indexOf('Date') >= 0) { + //here we will try to extract the ticks from the Date string in the "value" fields of JSON returned data + a = /^\/Date\((-?[0-9]+)\)\/$/.exec(value); + if (a) { + return parseInt(a[1], 10); + } + } + return value; + } + } + + for (var prop in data) { + line = []; + temp = data[prop]; + switch (prop) { + case "PriceTicks": + for (i=0; i<temp.length; i++) { + line.push([temp[i]['TickDate'], temp[i]['Price']]); + } + break; + case "PriceBars": + for (i=0; i<temp.length; i++) { + line.push([temp[i]['BarDate'], temp[i]['Open'], temp[i]['High'], temp[i]['Low'], temp[i]['Close']]); + } + break; + } + ret.push(line); + } + return ret; + }; +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.ciParser.min.js b/web/extensions/jqplot/plugins/jqplot.ciParser.min.js new file mode 100644 index 0000000..08f46c8 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.ciParser.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.ciParser=function(g,l){var m=[],o,n,h,f,e,c;if(typeof(g)=="string"){g=a.jqplot.JSON.parse(g,d)}else{if(typeof(g)=="object"){for(e in g){for(h=0;h<g[e].length;h++){for(c in g[e][h]){g[e][h][c]=d(c,g[e][h][c])}}}}else{return null}}function d(j,k){var i;if(k!=null){if(k.toString().indexOf("Date")>=0){i=/^\/Date\((-?[0-9]+)\)\/$/.exec(k);if(i){return parseInt(i[1],10)}}return k}}for(var b in g){o=[];n=g[b];switch(b){case"PriceTicks":for(h=0;h<n.length;h++){o.push([n[h]["TickDate"],n[h]["Price"]])}break;case"PriceBars":for(h=0;h<n.length;h++){o.push([n[h]["BarDate"],n[h]["Open"],n[h]["High"],n[h]["Low"],n[h]["Close"]])}break}m.push(o)}return m}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.cursor.js b/web/extensions/jqplot/plugins/jqplot.cursor.js new file mode 100644 index 0000000..bf1c36f --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.cursor.js @@ -0,0 +1,1108 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + /** + * Class: $.jqplot.Cursor + * Plugin class representing the cursor as displayed on the plot. + */ + $.jqplot.Cursor = function(options) { + // Group: Properties + // + // prop: style + // CSS spec for cursor style + this.style = 'crosshair'; + this.previousCursor = 'auto'; + // prop: show + // whether to show the cursor or not. + this.show = $.jqplot.config.enablePlugins; + // prop: showTooltip + // show a cursor position tooltip. Location of the tooltip + // will be controlled by followMouse and tooltipLocation. + this.showTooltip = true; + // prop: followMouse + // Tooltip follows the mouse, it is not at a fixed location. + // Tooltip will show on the grid at the location given by + // tooltipLocation, offset from the grid edge by tooltipOffset. + this.followMouse = false; + // prop: tooltipLocation + // Where to position tooltip. If followMouse is true, this is + // relative to the cursor, otherwise, it is relative to the grid. + // One of 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' + this.tooltipLocation = 'se'; + // prop: tooltipOffset + // Pixel offset of tooltip from the grid boudaries or cursor center. + this.tooltipOffset = 6; + // prop: showTooltipGridPosition + // show the grid pixel coordinates of the mouse. + this.showTooltipGridPosition = false; + // prop: showTooltipUnitPosition + // show the unit (data) coordinates of the mouse. + this.showTooltipUnitPosition = true; + // prop: showTooltipDataPosition + // Used with showVerticalLine to show intersecting data points in the tooltip. + this.showTooltipDataPosition = false; + // prop: tooltipFormatString + // sprintf format string for the tooltip. + // Uses Ash Searle's javascript sprintf implementation + // found here: http://hexmen.com/blog/2007/03/printf-sprintf/ + // See http://perldoc.perl.org/functions/sprintf.html for reference + // Note, if showTooltipDataPosition is true, the default tooltipFormatString + // will be set to the cursorLegendFormatString, not the default given here. + this.tooltipFormatString = '%.4P, %.4P'; + // prop: useAxesFormatters + // Use the x and y axes formatters to format the text in the tooltip. + this.useAxesFormatters = true; + // prop: tooltipAxisGroups + // Show position for the specified axes. + // This is an array like [['xaxis', 'yaxis'], ['xaxis', 'y2axis']] + // Default is to compute automatically for all visible axes. + this.tooltipAxisGroups = []; + // prop: zoom + // Enable plot zooming. + this.zoom = false; + // zoomProxy and zoomTarget properties are not directly set by user. + // They Will be set through call to zoomProxy method. + this.zoomProxy = false; + this.zoomTarget = false; + // prop: looseZoom + // Will expand zoom range to provide more rounded tick values. + // Works only with linear, log and date axes. + this.looseZoom = true; + // prop: clickReset + // Will reset plot zoom if single click on plot without drag. + this.clickReset = false; + // prop: dblClickReset + // Will reset plot zoom if double click on plot without drag. + this.dblClickReset = true; + // prop: showVerticalLine + // draw a vertical line across the plot which follows the cursor. + // When the line is near a data point, a special legend and/or tooltip can + // be updated with the data values. + this.showVerticalLine = false; + // prop: showHorizontalLine + // draw a horizontal line across the plot which follows the cursor. + this.showHorizontalLine = false; + // prop: constrainZoomTo + // 'none', 'x' or 'y' + this.constrainZoomTo = 'none'; + // // prop: autoscaleConstraint + // // when a constrained axis is specified, true will + // // auatoscale the adjacent axis. + // this.autoscaleConstraint = true; + this.shapeRenderer = new $.jqplot.ShapeRenderer(); + this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}}; + this._tooltipElem; + this.zoomCanvas; + this.cursorCanvas; + // prop: intersectionThreshold + // pixel distance from data point or marker to consider cursor lines intersecting with point. + // If data point markers are not shown, this should be >= 1 or will often miss point intersections. + this.intersectionThreshold = 2; + // prop: showCursorLegend + // Replace the plot legend with an enhanced legend displaying intersection information. + this.showCursorLegend = false; + // prop: cursorLegendFormatString + // Format string used in the cursor legend. If showTooltipDataPosition is true, + // this will also be the default format string used by tooltipFormatString. + this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString; + // whether the cursor is over the grid or not. + this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null}; + // prop: constrainOutsideZoom + // True to limit actual zoom area to edges of grid, even when zooming + // outside of plot area. That is, can't zoom out by mousing outside plot. + this.constrainOutsideZoom = true; + // prop: showTooltipOutsideZoom + // True will keep updating the tooltip when zooming of the grid. + this.showTooltipOutsideZoom = false; + // true if mouse is over grid, false if not. + this.onGrid = false; + $.extend(true, this, options); + }; + + $.jqplot.Cursor.cursorLegendFormatString = '%s x:%s, y:%s'; + + // called with scope of plot + $.jqplot.Cursor.init = function (target, data, opts){ + // add a cursor attribute to the plot + var options = opts || {}; + this.plugins.cursor = new $.jqplot.Cursor(options.cursor); + var c = this.plugins.cursor; + + if (c.show) { + $.jqplot.eventListenerHooks.push(['jqplotMouseEnter', handleMouseEnter]); + $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]); + $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]); + + if (c.showCursorLegend) { + opts.legend = opts.legend || {}; + opts.legend.renderer = $.jqplot.CursorLegendRenderer; + opts.legend.formatString = this.plugins.cursor.cursorLegendFormatString; + opts.legend.show = true; + } + + if (c.zoom) { + $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]); + + if (c.clickReset) { + $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]); + } + + if (c.dblClickReset) { + $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]); + } + } + + this.resetZoom = function() { + var axes = this.axes; + if (!c.zoomProxy) { + for (var ax in axes) { + axes[ax].reset(); + axes[ax]._ticks = []; + // fake out tick creation algorithm to make sure original auto + // computed format string is used if _overrideFormatString is true + if (c._zoom.axes[ax] !== undefined) { + axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString; + } + } + this.redraw(); + } + else { + var ctx = this.plugins.cursor.zoomCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx = null; + } + this.plugins.cursor._zoom.isZoomed = false; + this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]); + }; + + + if (c.showTooltipDataPosition) { + c.showTooltipUnitPosition = false; + c.showTooltipGridPosition = false; + if (options.cursor.tooltipFormatString == undefined) { + c.tooltipFormatString = $.jqplot.Cursor.cursorLegendFormatString; + } + } + } + }; + + // called with context of plot + $.jqplot.Cursor.postDraw = function() { + var c = this.plugins.cursor; + + // Memory Leaks patch + if (c.zoomCanvas) { + c.zoomCanvas.resetCanvas(); + c.zoomCanvas = null; + } + + if (c.cursorCanvas) { + c.cursorCanvas.resetCanvas(); + c.cursorCanvas = null; + } + + if (c._tooltipElem) { + c._tooltipElem.emptyForce(); + c._tooltipElem = null; + } + + + if (c.zoom) { + c.zoomCanvas = new $.jqplot.GenericCanvas(); + this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this)); + c.zoomCanvas.setContext(); + } + + var elem = document.createElement('div'); + c._tooltipElem = $(elem); + elem = null; + c._tooltipElem.addClass('jqplot-cursor-tooltip'); + c._tooltipElem.css({position:'absolute', display:'none'}); + + + if (c.zoomCanvas) { + c.zoomCanvas._elem.before(c._tooltipElem); + } + + else { + this.eventCanvas._elem.before(c._tooltipElem); + } + + if (c.showVerticalLine || c.showHorizontalLine) { + c.cursorCanvas = new $.jqplot.GenericCanvas(); + this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions, this)); + c.cursorCanvas.setContext(); + } + + // if we are showing the positions in unit coordinates, and no axes groups + // were specified, create a default set. + if (c.showTooltipUnitPosition){ + if (c.tooltipAxisGroups.length === 0) { + var series = this.series; + var s; + var temp = []; + for (var i=0; i<series.length; i++) { + s = series[i]; + var ax = s.xaxis+','+s.yaxis; + if ($.inArray(ax, temp) == -1) { + temp.push(ax); + } + } + for (var i=0; i<temp.length; i++) { + c.tooltipAxisGroups.push(temp[i].split(',')); + } + } + } + }; + + // Group: methods + // + // method: $.jqplot.Cursor.zoomProxy + // links targetPlot to controllerPlot so that plot zooming of + // targetPlot will be controlled by zooming on the controllerPlot. + // controllerPlot will not actually zoom, but acts as an + // overview plot. Note, the zoom options must be set to true for + // zoomProxy to work. + $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) { + var tc = targetPlot.plugins.cursor; + var cc = controllerPlot.plugins.cursor; + tc.zoomTarget = true; + tc.zoom = true; + tc.style = 'auto'; + tc.dblClickReset = false; + cc.zoom = true; + cc.zoomProxy = true; + + controllerPlot.target.bind('jqplotZoom', plotZoom); + controllerPlot.target.bind('jqplotResetZoom', plotReset); + + function plotZoom(ev, gridpos, datapos, plot, cursor) { + tc.doZoom(gridpos, datapos, targetPlot, cursor); + } + + function plotReset(ev, plot, cursor) { + targetPlot.resetZoom(); + } + }; + + $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) { + var axes = plot.axes; + var cax = cursor._zoom.axes; + if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) { + for (var ax in axes) { + // axes[ax]._ticks = []; + // axes[ax].min = cax[ax].min; + // axes[ax].max = cax[ax].max; + // axes[ax].numberTicks = cax[ax].numberTicks; + // axes[ax].tickInterval = cax[ax].tickInterval; + // // for date axes + // axes[ax].daTickInterval = cax[ax].daTickInterval; + axes[ax].reset(); + axes[ax]._ticks = []; + // fake out tick creation algorithm to make sure original auto + // computed format string is used if _overrideFormatString is true + axes[ax]._autoFormatString = cax[ax].tickFormatString; + } + plot.redraw(); + cursor._zoom.isZoomed = false; + } + else { + var ctx = cursor.zoomCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx = null; + } + plot.target.trigger('jqplotResetZoom', [plot, cursor]); + }; + + $.jqplot.Cursor.resetZoom = function(plot) { + plot.resetZoom(); + }; + + $.jqplot.Cursor.prototype.doZoom = function (gridpos, datapos, plot, cursor) { + var c = cursor; + var axes = plot.axes; + var zaxes = c._zoom.axes; + var start = zaxes.start; + var end = zaxes.end; + var min, max, dp, span, + newmin, newmax, curax, _numberTicks, ret; + var ctx = plot.plugins.cursor.zoomCanvas._ctx; + // don't zoom if zoom area is too small (in pixels) + if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) || (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) { + if (!plot.plugins.cursor.zoomProxy) { + for (var ax in datapos) { + // make a copy of the original axes to revert back. + if (c._zoom.axes[ax] == undefined) { + c._zoom.axes[ax] = {}; + c._zoom.axes[ax].numberTicks = axes[ax].numberTicks; + c._zoom.axes[ax].tickInterval = axes[ax].tickInterval; + // for date axes... + c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval; + c._zoom.axes[ax].min = axes[ax].min; + c._zoom.axes[ax].max = axes[ax].max; + c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString : ''; + } + + + if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) { + dp = datapos[ax]; + if (dp != null) { + if (dp > start[ax]) { + newmin = start[ax]; + newmax = dp; + } + else { + span = start[ax] - dp; + newmin = dp; + newmax = start[ax]; + } + + curax = axes[ax]; + + _numberTicks = null; + + // if aligning this axis, use number of ticks from previous axis. + // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? + if (curax.alignTicks) { + if (curax.name === 'x2axis' && plot.axes.xaxis.show) { + _numberTicks = plot.axes.xaxis.numberTicks; + } + else if (curax.name.charAt(0) === 'y' && curax.name !== 'yaxis' && curax.name !== 'yMidAxis' && plot.axes.yaxis.show) { + _numberTicks = plot.axes.yaxis.numberTicks; + } + } + + if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer )) { //} || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) { + + ret = $.jqplot.LinearTickGenerator(newmin, newmax, curax._scalefact, _numberTicks); + + // if new minimum is less than "true" minimum of axis display, adjust it + if (axes[ax].tickInset && ret[0] < axes[ax].min + axes[ax].tickInset * axes[ax].tickInterval) { + ret[0] += ret[4]; + ret[2] -= 1; + } + + // if new maximum is greater than "true" max of axis display, adjust it + if (axes[ax].tickInset && ret[1] > axes[ax].max - axes[ax].tickInset * axes[ax].tickInterval) { + ret[1] -= ret[4]; + ret[2] -= 1; + } + + // for log axes, don't fall below current minimum, this will look bad and can't have 0 in range anyway. + if (axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer && ret[0] < axes[ax].min) { + // remove a tick and shift min up + ret[0] += ret[4]; + ret[2] -= 1; + } + + axes[ax].min = ret[0]; + axes[ax].max = ret[1]; + axes[ax]._autoFormatString = ret[3]; + axes[ax].numberTicks = ret[2]; + axes[ax].tickInterval = ret[4]; + // for date axes... + axes[ax].daTickInterval = [ret[4]/1000, 'seconds']; + } + else { + axes[ax].min = newmin; + axes[ax].max = newmax; + axes[ax].tickInterval = null; + axes[ax].numberTicks = null; + // for date axes... + axes[ax].daTickInterval = null; + } + + axes[ax]._ticks = []; + } + } + + // if ((c.constrainZoomTo == 'x' && ax.charAt(0) == 'y' && c.autoscaleConstraint) || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'x' && c.autoscaleConstraint)) { + // dp = datapos[ax]; + // if (dp != null) { + // axes[ax].max == null; + // axes[ax].min = null; + // } + // } + } + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + plot.redraw(); + c._zoom.isZoomed = true; + ctx = null; + } + plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]); + } + }; + + $.jqplot.preInitHooks.push($.jqplot.Cursor.init); + $.jqplot.postDrawHooks.push($.jqplot.Cursor.postDraw); + + function updateTooltip(gridpos, datapos, plot) { + var c = plot.plugins.cursor; + var s = ''; + var addbr = false; + if (c.showTooltipGridPosition) { + s = gridpos.x+', '+gridpos.y; + addbr = true; + } + if (c.showTooltipUnitPosition) { + var g; + for (var i=0; i<c.tooltipAxisGroups.length; i++) { + g = c.tooltipAxisGroups[i]; + if (addbr) { + s += '<br />'; + } + if (c.useAxesFormatters) { + for (var j=0; j<g.length; j++) { + if (j) { + s += ', '; + } + var af = plot.axes[g[j]]._ticks[0].formatter; + var afstr = plot.axes[g[j]]._ticks[0].formatString; + s += af(afstr, datapos[g[j]]); + } + } + else { + s += $.jqplot.sprintf(c.tooltipFormatString, datapos[g[0]], datapos[g[1]]); + } + addbr = true; + } + } + + if (c.showTooltipDataPosition) { + var series = plot.series; + var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y); + var addbr = false; + + for (var i = 0; i< series.length; i++) { + if (series[i].show) { + var idx = series[i].index; + var label = series[i].label.toString(); + var cellid = $.inArray(idx, ret.indices); + var sx = undefined; + var sy = undefined; + if (cellid != -1) { + var data = ret.data[cellid].data; + if (c.useAxesFormatters) { + var xf = series[i]._xaxis._ticks[0].formatter; + var yf = series[i]._yaxis._ticks[0].formatter; + var xfstr = series[i]._xaxis._ticks[0].formatString; + var yfstr = series[i]._yaxis._ticks[0].formatString; + sx = xf(xfstr, data[0]); + sy = yf(yfstr, data[1]); + } + else { + sx = data[0]; + sy = data[1]; + } + if (addbr) { + s += '<br />'; + } + s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy); + addbr = true; + } + } + } + + } + c._tooltipElem.html(s); + } + + function moveLine(gridpos, plot) { + var c = plot.plugins.cursor; + var ctx = c.cursorCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + if (c.showVerticalLine) { + c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]); + } + if (c.showHorizontalLine) { + c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]); + } + var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y); + if (c.showCursorLegend) { + var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); + for (var i=0; i<cells.length; i++) { + var idx = $(cells[i]).data('seriesIndex'); + var series = plot.series[idx]; + var label = series.label.toString(); + var cellid = $.inArray(idx, ret.indices); + var sx = undefined; + var sy = undefined; + if (cellid != -1) { + var data = ret.data[cellid].data; + if (c.useAxesFormatters) { + var xf = series._xaxis._ticks[0].formatter; + var yf = series._yaxis._ticks[0].formatter; + var xfstr = series._xaxis._ticks[0].formatString; + var yfstr = series._yaxis._ticks[0].formatString; + sx = xf(xfstr, data[0]); + sy = yf(yfstr, data[1]); + } + else { + sx = data[0]; + sy = data[1]; + } + } + if (plot.legend.escapeHtml) { + $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy)); + } + else { + $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy)); + } + } + } + ctx = null; + } + + function getIntersectingPoints(plot, x, y) { + var ret = {indices:[], data:[]}; + var s, i, d0, d, j, r, p; + var threshold; + var c = plot.plugins.cursor; + for (var i=0; i<plot.series.length; i++) { + s = plot.series[i]; + r = s.renderer; + if (s.show) { + threshold = c.intersectionThreshold; + if (s.showMarker) { + threshold += s.markerRenderer.size/2; + } + for (var j=0; j<s.gridData.length; j++) { + p = s.gridData[j]; + // check vertical line + if (c.showVerticalLine) { + if (Math.abs(x-p[0]) <= threshold) { + ret.indices.push(i); + ret.data.push({seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}); + } + } + } + } + } + return ret; + } + + function moveTooltip(gridpos, plot) { + var c = plot.plugins.cursor; + var elem = c._tooltipElem; + switch (c.tooltipLocation) { + case 'nw': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); + break; + case 'n': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; + var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); + break; + case 'ne': + var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); + break; + case 'e': + var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + case 'se': + var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; + break; + case 's': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; + var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; + break; + case 'sw': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; + break; + case 'w': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + default: + var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; + var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; + break; + } + + elem.css('left', x); + elem.css('top', y); + elem = null; + } + + function positionTooltip(plot) { + // fake a grid for positioning + var grid = plot._gridPadding; + var c = plot.plugins.cursor; + var elem = c._tooltipElem; + switch (c.tooltipLocation) { + case 'nw': + var a = grid.left + c.tooltipOffset; + var b = grid.top + c.tooltipOffset; + elem.css('left', a); + elem.css('top', b); + break; + case 'n': + var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2; + var b = grid.top + c.tooltipOffset; + elem.css('left', a); + elem.css('top', b); + break; + case 'ne': + var a = grid.right + c.tooltipOffset; + var b = grid.top + c.tooltipOffset; + elem.css({right:a, top:b}); + break; + case 'e': + var a = grid.right + c.tooltipOffset; + var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2; + elem.css({right:a, top:b}); + break; + case 'se': + var a = grid.right + c.tooltipOffset; + var b = grid.bottom + c.tooltipOffset; + elem.css({right:a, bottom:b}); + break; + case 's': + var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2; + var b = grid.bottom + c.tooltipOffset; + elem.css({left:a, bottom:b}); + break; + case 'sw': + var a = grid.left + c.tooltipOffset; + var b = grid.bottom + c.tooltipOffset; + elem.css({left:a, bottom:b}); + break; + case 'w': + var a = grid.left + c.tooltipOffset; + var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2; + elem.css({left:a, top:b}); + break; + default: // same as 'se' + var a = grid.right - c.tooltipOffset; + var b = grid.bottom + c.tooltipOffset; + elem.css({right:a, bottom:b}); + break; + } + elem = null; + } + + function handleClick (ev, gridpos, datapos, neighbor, plot) { + ev.preventDefault(); + ev.stopImmediatePropagation(); + var c = plot.plugins.cursor; + if (c.clickReset) { + c.resetZoom(plot, c); + } + var sel = window.getSelection; + if (document.selection && document.selection.empty) + { + document.selection.empty(); + } + else if (sel && !sel().isCollapsed) { + sel().collapse(); + } + return false; + } + + function handleDblClick (ev, gridpos, datapos, neighbor, plot) { + ev.preventDefault(); + ev.stopImmediatePropagation(); + var c = plot.plugins.cursor; + if (c.dblClickReset) { + c.resetZoom(plot, c); + } + var sel = window.getSelection; + if (document.selection && document.selection.empty) + { + document.selection.empty(); + } + else if (sel && !sel().isCollapsed) { + sel().collapse(); + } + return false; + } + + function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) { + var c = plot.plugins.cursor; + c.onGrid = false; + if (c.show) { + $(ev.target).css('cursor', c.previousCursor); + if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) { + c._tooltipElem.empty(); + c._tooltipElem.hide(); + } + if (c.zoom) { + c._zoom.gridpos = gridpos; + c._zoom.datapos = datapos; + } + if (c.showVerticalLine || c.showHorizontalLine) { + var ctx = c.cursorCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx = null; + } + if (c.showCursorLegend) { + var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); + for (var i=0; i<cells.length; i++) { + var idx = $(cells[i]).data('seriesIndex'); + var series = plot.series[idx]; + var label = series.label.toString(); + if (plot.legend.escapeHtml) { + $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); + } + else { + $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); + } + + } + } + } + } + + function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) { + var c = plot.plugins.cursor; + c.onGrid = true; + if (c.show) { + c.previousCursor = ev.target.style.cursor; + ev.target.style.cursor = c.style; + if (c.showTooltip) { + updateTooltip(gridpos, datapos, plot); + if (c.followMouse) { + moveTooltip(gridpos, plot); + } + else { + positionTooltip(plot); + } + c._tooltipElem.show(); + } + if (c.showVerticalLine || c.showHorizontalLine) { + moveLine(gridpos, plot); + } + } + + } + + function handleMouseMove(ev, gridpos, datapos, neighbor, plot) { + var c = plot.plugins.cursor; + if (c.show) { + if (c.showTooltip) { + updateTooltip(gridpos, datapos, plot); + if (c.followMouse) { + moveTooltip(gridpos, plot); + } + } + if (c.showVerticalLine || c.showHorizontalLine) { + moveLine(gridpos, plot); + } + } + } + + function getEventPosition(ev) { + var plot = ev.data.plot; + var go = plot.eventCanvas._elem.offset(); + var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; + ////// + // TO DO: handle yMidAxis + ////// + var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null}; + var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; + var ax = plot.axes; + var n, axis; + for (n=11; n>0; n--) { + axis = an[n-1]; + if (ax[axis].show) { + dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); + } + } + + return {offsets:go, gridPos:gridPos, dataPos:dataPos}; + } + + function handleZoomMove(ev) { + var plot = ev.data.plot; + var c = plot.plugins.cursor; + // don't do anything if not on grid. + if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) { + ev.preventDefault(); + var ctx = c.zoomCanvas._ctx; + var positions = getEventPosition(ev); + var gridpos = positions.gridPos; + var datapos = positions.dataPos; + c._zoom.gridpos = gridpos; + c._zoom.datapos = datapos; + c._zoom.zooming = true; + var xpos = gridpos.x; + var ypos = gridpos.y; + var height = ctx.canvas.height; + var width = ctx.canvas.width; + if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) { + updateTooltip(gridpos, datapos, plot); + if (c.followMouse) { + moveTooltip(gridpos, plot); + } + } + if (c.constrainZoomTo == 'x') { + c._zoom.end = [xpos, height]; + } + else if (c.constrainZoomTo == 'y') { + c._zoom.end = [width, ypos]; + } + else { + c._zoom.end = [xpos, ypos]; + } + var sel = window.getSelection; + if (document.selection && document.selection.empty) + { + document.selection.empty(); + } + else if (sel && !sel().isCollapsed) { + sel().collapse(); + } + drawZoomBox.call(c); + ctx = null; + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + var c = plot.plugins.cursor; + if(plot.plugins.mobile){ + $(document).one('vmouseup.jqplot_cursor', {plot:plot}, handleMouseUp); + } else { + $(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp); + } + var axes = plot.axes; + if (document.onselectstart != undefined) { + c._oldHandlers.onselectstart = document.onselectstart; + document.onselectstart = function () { return false; }; + } + if (document.ondrag != undefined) { + c._oldHandlers.ondrag = document.ondrag; + document.ondrag = function () { return false; }; + } + if (document.onmousedown != undefined) { + c._oldHandlers.onmousedown = document.onmousedown; + document.onmousedown = function () { return false; }; + } + if (c.zoom) { + if (!c.zoomProxy) { + var ctx = c.zoomCanvas._ctx; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx = null; + } + if (c.constrainZoomTo == 'x') { + c._zoom.start = [gridpos.x, 0]; + } + else if (c.constrainZoomTo == 'y') { + c._zoom.start = [0, gridpos.y]; + } + else { + c._zoom.start = [gridpos.x, gridpos.y]; + } + c._zoom.started = true; + for (var ax in datapos) { + // get zoom starting position. + c._zoom.axes.start[ax] = datapos[ax]; + } + if(plot.plugins.mobile){ + $(document).bind('vmousemove.jqplotCursor', {plot:plot}, handleZoomMove); + } else { + $(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove); + } + + } + } + + function handleMouseUp(ev) { + var plot = ev.data.plot; + var c = plot.plugins.cursor; + if (c.zoom && c._zoom.zooming && !c.zoomTarget) { + var xpos = c._zoom.gridpos.x; + var ypos = c._zoom.gridpos.y; + var datapos = c._zoom.datapos; + var height = c.zoomCanvas._ctx.canvas.height; + var width = c.zoomCanvas._ctx.canvas.width; + var axes = plot.axes; + + if (c.constrainOutsideZoom && !c.onGrid) { + if (xpos < 0) { xpos = 0; } + else if (xpos > width) { xpos = width; } + if (ypos < 0) { ypos = 0; } + else if (ypos > height) { ypos = height; } + + for (var axis in datapos) { + if (datapos[axis]) { + if (axis.charAt(0) == 'x') { + datapos[axis] = axes[axis].series_p2u(xpos); + } + else { + datapos[axis] = axes[axis].series_p2u(ypos); + } + } + } + } + + if (c.constrainZoomTo == 'x') { + ypos = height; + } + else if (c.constrainZoomTo == 'y') { + xpos = width; + } + c._zoom.end = [xpos, ypos]; + c._zoom.gridpos = {x:xpos, y:ypos}; + + c.doZoom(c._zoom.gridpos, datapos, plot, c); + } + c._zoom.started = false; + c._zoom.zooming = false; + + $(document).unbind('mousemove.jqplotCursor', handleZoomMove); + + if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){ + document.onselectstart = c._oldHandlers.onselectstart; + c._oldHandlers.onselectstart = null; + } + if (document.ondrag != undefined && c._oldHandlers.ondrag != null){ + document.ondrag = c._oldHandlers.ondrag; + c._oldHandlers.ondrag = null; + } + if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){ + document.onmousedown = c._oldHandlers.onmousedown; + c._oldHandlers.onmousedown = null; + } + + } + + function drawZoomBox() { + var start = this._zoom.start; + var end = this._zoom.end; + var ctx = this.zoomCanvas._ctx; + var l, t, h, w; + if (end[0] > start[0]) { + l = start[0]; + w = end[0] - start[0]; + } + else { + l = end[0]; + w = start[0] - end[0]; + } + if (end[1] > start[1]) { + t = start[1]; + h = end[1] - start[1]; + } + else { + t = end[1]; + h = start[1] - end[1]; + } + ctx.fillStyle = 'rgba(0,0,0,0.2)'; + ctx.strokeStyle = '#999999'; + ctx.lineWidth = 1.0; + ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height); + ctx.clearRect(l, t, w, h); + // IE won't show transparent fill rect, so stroke a rect also. + ctx.strokeRect(l,t,w,h); + ctx = null; + } + + $.jqplot.CursorLegendRenderer = function(options) { + $.jqplot.TableLegendRenderer.call(this, options); + this.formatString = '%s'; + }; + + $.jqplot.CursorLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.CursorLegendRenderer.prototype.constructor = $.jqplot.CursorLegendRenderer; + + // called in context of a Legend + $.jqplot.CursorLegendRenderer.prototype.draw = function() { + if (this._elem) { + this._elem.emptyForce(); + this._elem = null; + } + if (this.show) { + var series = this._series, s; + // make a table. one line label per row. + var elem = document.createElement('table'); + this._elem = $(elem); + elem = null; + this._elem.addClass('jqplot-legend jqplot-cursor-legend'); + this._elem.css('position', 'absolute'); + + var pad = false; + for (var i = 0; i< series.length; i++) { + s = series[i]; + if (s.show && s.showLabel) { + var lt = $.jqplot.sprintf(this.formatString, s.label.toString()); + if (lt) { + var color = s.color; + if (s._stack && !s.fill) { + color = ''; + } + addrow.call(this, lt, color, pad, i); + pad = true; + } + // let plugins add more rows to legend. Used by trend line plugin. + for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { + var item = $.jqplot.addLegendRowHooks[j].call(this, s); + if (item) { + addrow.call(this, item.label, item.color, pad); + pad = true; + } + } + } + } + series = s = null; + delete series; + delete s; + } + + function addrow(label, color, pad, idx) { + var rs = (pad) ? this.rowSpacing : '0'; + var tr = $('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem); + tr.data('seriesIndex', idx); + $('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+rs+';">'+ + '<div style="border:1px solid #cccccc;padding:0.2em;">'+ + '<div class="jqplot-cursor-legend-swatch" style="background-color:'+color+';"></div>'+ + '</div></td>').appendTo(tr); + var td = $('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+rs+';"></td>'); + td.appendTo(tr); + td.data('seriesIndex', idx); + if (this.escapeHtml) { + td.text(label); + } + else { + td.html(label); + } + tr = null; + td = null; + } + return this._elem; + }; + +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.cursor.min.js b/web/extensions/jqplot/plugins/jqplot.cursor.min.js new file mode 100644 index 0000000..51580ba --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.cursor.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(j){j.jqplot.Cursor=function(q){this.style="crosshair";this.previousCursor="auto";this.show=j.jqplot.config.enablePlugins;this.showTooltip=true;this.followMouse=false;this.tooltipLocation="se";this.tooltipOffset=6;this.showTooltipGridPosition=false;this.showTooltipUnitPosition=true;this.showTooltipDataPosition=false;this.tooltipFormatString="%.4P, %.4P";this.useAxesFormatters=true;this.tooltipAxisGroups=[];this.zoom=false;this.zoomProxy=false;this.zoomTarget=false;this.looseZoom=true;this.clickReset=false;this.dblClickReset=true;this.showVerticalLine=false;this.showHorizontalLine=false;this.constrainZoomTo="none";this.shapeRenderer=new j.jqplot.ShapeRenderer();this._zoom={start:[],end:[],started:false,zooming:false,isZoomed:false,axes:{start:{},end:{}},gridpos:{},datapos:{}};this._tooltipElem;this.zoomCanvas;this.cursorCanvas;this.intersectionThreshold=2;this.showCursorLegend=false;this.cursorLegendFormatString=j.jqplot.Cursor.cursorLegendFormatString;this._oldHandlers={onselectstart:null,ondrag:null,onmousedown:null};this.constrainOutsideZoom=true;this.showTooltipOutsideZoom=false;this.onGrid=false;j.extend(true,this,q)};j.jqplot.Cursor.cursorLegendFormatString="%s x:%s, y:%s";j.jqplot.Cursor.init=function(t,s,r){var q=r||{};this.plugins.cursor=new j.jqplot.Cursor(q.cursor);var u=this.plugins.cursor;if(u.show){j.jqplot.eventListenerHooks.push(["jqplotMouseEnter",b]);j.jqplot.eventListenerHooks.push(["jqplotMouseLeave",f]);j.jqplot.eventListenerHooks.push(["jqplotMouseMove",i]);if(u.showCursorLegend){r.legend=r.legend||{};r.legend.renderer=j.jqplot.CursorLegendRenderer;r.legend.formatString=this.plugins.cursor.cursorLegendFormatString;r.legend.show=true}if(u.zoom){j.jqplot.eventListenerHooks.push(["jqplotMouseDown",a]);if(u.clickReset){j.jqplot.eventListenerHooks.push(["jqplotClick",k])}if(u.dblClickReset){j.jqplot.eventListenerHooks.push(["jqplotDblClick",c])}}this.resetZoom=function(){var x=this.axes;if(!u.zoomProxy){for(var w in x){x[w].reset();x[w]._ticks=[];if(u._zoom.axes[w]!==undefined){x[w]._autoFormatString=u._zoom.axes[w].tickFormatString}}this.redraw()}else{var v=this.plugins.cursor.zoomCanvas._ctx;v.clearRect(0,0,v.canvas.width,v.canvas.height);v=null}this.plugins.cursor._zoom.isZoomed=false;this.target.trigger("jqplotResetZoom",[this,this.plugins.cursor])};if(u.showTooltipDataPosition){u.showTooltipUnitPosition=false;u.showTooltipGridPosition=false;if(q.cursor.tooltipFormatString==undefined){u.tooltipFormatString=j.jqplot.Cursor.cursorLegendFormatString}}}};j.jqplot.Cursor.postDraw=function(){var x=this.plugins.cursor;if(x.zoomCanvas){x.zoomCanvas.resetCanvas();x.zoomCanvas=null}if(x.cursorCanvas){x.cursorCanvas.resetCanvas();x.cursorCanvas=null}if(x._tooltipElem){x._tooltipElem.emptyForce();x._tooltipElem=null}if(x.zoom){x.zoomCanvas=new j.jqplot.GenericCanvas();this.eventCanvas._elem.before(x.zoomCanvas.createElement(this._gridPadding,"jqplot-zoom-canvas",this._plotDimensions,this));x.zoomCanvas.setContext()}var v=document.createElement("div");x._tooltipElem=j(v);v=null;x._tooltipElem.addClass("jqplot-cursor-tooltip");x._tooltipElem.css({position:"absolute",display:"none"});if(x.zoomCanvas){x.zoomCanvas._elem.before(x._tooltipElem)}else{this.eventCanvas._elem.before(x._tooltipElem)}if(x.showVerticalLine||x.showHorizontalLine){x.cursorCanvas=new j.jqplot.GenericCanvas();this.eventCanvas._elem.before(x.cursorCanvas.createElement(this._gridPadding,"jqplot-cursor-canvas",this._plotDimensions,this));x.cursorCanvas.setContext()}if(x.showTooltipUnitPosition){if(x.tooltipAxisGroups.length===0){var t=this.series;var u;var q=[];for(var r=0;r<t.length;r++){u=t[r];var w=u.xaxis+","+u.yaxis;if(j.inArray(w,q)==-1){q.push(w)}}for(var r=0;r<q.length;r++){x.tooltipAxisGroups.push(q[r].split(","))}}}};j.jqplot.Cursor.zoomProxy=function(v,r){var q=v.plugins.cursor;var u=r.plugins.cursor;q.zoomTarget=true;q.zoom=true;q.style="auto";q.dblClickReset=false;u.zoom=true;u.zoomProxy=true;r.target.bind("jqplotZoom",t);r.target.bind("jqplotResetZoom",s);function t(x,w,z,y,A){q.doZoom(w,z,v,A)}function s(w,x,y){v.resetZoom()}};j.jqplot.Cursor.prototype.resetZoom=function(u,v){var t=u.axes;var s=v._zoom.axes;if(!u.plugins.cursor.zoomProxy&&v._zoom.isZoomed){for(var r in t){t[r].reset();t[r]._ticks=[];t[r]._autoFormatString=s[r].tickFormatString}u.redraw();v._zoom.isZoomed=false}else{var q=v.zoomCanvas._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);q=null}u.target.trigger("jqplotResetZoom",[u,v])};j.jqplot.Cursor.resetZoom=function(q){q.resetZoom()};j.jqplot.Cursor.prototype.doZoom=function(G,t,C,u){var I=u;var F=C.axes;var r=I._zoom.axes;var w=r.start;var s=r.end;var B,E,z,D,v,x,q,H,J;var A=C.plugins.cursor.zoomCanvas._ctx;if((I.constrainZoomTo=="none"&&Math.abs(G.x-I._zoom.start[0])>6&&Math.abs(G.y-I._zoom.start[1])>6)||(I.constrainZoomTo=="x"&&Math.abs(G.x-I._zoom.start[0])>6)||(I.constrainZoomTo=="y"&&Math.abs(G.y-I._zoom.start[1])>6)){if(!C.plugins.cursor.zoomProxy){for(var y in t){if(I._zoom.axes[y]==undefined){I._zoom.axes[y]={};I._zoom.axes[y].numberTicks=F[y].numberTicks;I._zoom.axes[y].tickInterval=F[y].tickInterval;I._zoom.axes[y].daTickInterval=F[y].daTickInterval;I._zoom.axes[y].min=F[y].min;I._zoom.axes[y].max=F[y].max;I._zoom.axes[y].tickFormatString=(F[y].tickOptions!=null)?F[y].tickOptions.formatString:""}if((I.constrainZoomTo=="none")||(I.constrainZoomTo=="x"&&y.charAt(0)=="x")||(I.constrainZoomTo=="y"&&y.charAt(0)=="y")){z=t[y];if(z!=null){if(z>w[y]){v=w[y];x=z}else{D=w[y]-z;v=z;x=w[y]}q=F[y];H=null;if(q.alignTicks){if(q.name==="x2axis"&&C.axes.xaxis.show){H=C.axes.xaxis.numberTicks}else{if(q.name.charAt(0)==="y"&&q.name!=="yaxis"&&q.name!=="yMidAxis"&&C.axes.yaxis.show){H=C.axes.yaxis.numberTicks}}}if(this.looseZoom&&(F[y].renderer.constructor===j.jqplot.LinearAxisRenderer||F[y].renderer.constructor===j.jqplot.LogAxisRenderer)){J=j.jqplot.LinearTickGenerator(v,x,q._scalefact,H);if(F[y].tickInset&&J[0]<F[y].min+F[y].tickInset*F[y].tickInterval){J[0]+=J[4];J[2]-=1}if(F[y].tickInset&&J[1]>F[y].max-F[y].tickInset*F[y].tickInterval){J[1]-=J[4];J[2]-=1}if(F[y].renderer.constructor===j.jqplot.LogAxisRenderer&&J[0]<F[y].min){J[0]+=J[4];J[2]-=1}F[y].min=J[0];F[y].max=J[1];F[y]._autoFormatString=J[3];F[y].numberTicks=J[2];F[y].tickInterval=J[4];F[y].daTickInterval=[J[4]/1000,"seconds"]}else{F[y].min=v;F[y].max=x;F[y].tickInterval=null;F[y].numberTicks=null;F[y].daTickInterval=null}F[y]._ticks=[]}}}A.clearRect(0,0,A.canvas.width,A.canvas.height);C.redraw();I._zoom.isZoomed=true;A=null}C.target.trigger("jqplotZoom",[G,t,C,u])}};j.jqplot.preInitHooks.push(j.jqplot.Cursor.init);j.jqplot.postDrawHooks.push(j.jqplot.Cursor.postDraw);function e(G,r,C){var J=C.plugins.cursor;var w="";var N=false;if(J.showTooltipGridPosition){w=G.x+", "+G.y;N=true}if(J.showTooltipUnitPosition){var F;for(var E=0;E<J.tooltipAxisGroups.length;E++){F=J.tooltipAxisGroups[E];if(N){w+="<br />"}if(J.useAxesFormatters){for(var D=0;D<F.length;D++){if(D){w+=", "}var H=C.axes[F[D]]._ticks[0].formatter;var B=C.axes[F[D]]._ticks[0].formatString;w+=H(B,r[F[D]])}}else{w+=j.jqplot.sprintf(J.tooltipFormatString,r[F[0]],r[F[1]])}N=true}}if(J.showTooltipDataPosition){var u=C.series;var M=d(C,G.x,G.y);var N=false;for(var E=0;E<u.length;E++){if(u[E].show){var y=u[E].index;var t=u[E].label.toString();var I=j.inArray(y,M.indices);var z=undefined;var x=undefined;if(I!=-1){var L=M.data[I].data;if(J.useAxesFormatters){var A=u[E]._xaxis._ticks[0].formatter;var q=u[E]._yaxis._ticks[0].formatter;var K=u[E]._xaxis._ticks[0].formatString;var v=u[E]._yaxis._ticks[0].formatString;z=A(K,L[0]);x=q(v,L[1])}else{z=L[0];x=L[1]}if(N){w+="<br />"}w+=j.jqplot.sprintf(J.tooltipFormatString,t,z,x);N=true}}}}J._tooltipElem.html(w)}function g(C,A){var E=A.plugins.cursor;var z=E.cursorCanvas._ctx;z.clearRect(0,0,z.canvas.width,z.canvas.height);if(E.showVerticalLine){E.shapeRenderer.draw(z,[[C.x,0],[C.x,z.canvas.height]])}if(E.showHorizontalLine){E.shapeRenderer.draw(z,[[0,C.y],[z.canvas.width,C.y]])}var G=d(A,C.x,C.y);if(E.showCursorLegend){var r=j(A.targetId+" td.jqplot-cursor-legend-label");for(var B=0;B<r.length;B++){var v=j(r[B]).data("seriesIndex");var t=A.series[v];var s=t.label.toString();var D=j.inArray(v,G.indices);var x=undefined;var w=undefined;if(D!=-1){var H=G.data[D].data;if(E.useAxesFormatters){var y=t._xaxis._ticks[0].formatter;var q=t._yaxis._ticks[0].formatter;var F=t._xaxis._ticks[0].formatString;var u=t._yaxis._ticks[0].formatString;x=y(F,H[0]);w=q(u,H[1])}else{x=H[0];w=H[1]}}if(A.legend.escapeHtml){j(r[B]).text(j.jqplot.sprintf(E.cursorLegendFormatString,s,x,w))}else{j(r[B]).html(j.jqplot.sprintf(E.cursorLegendFormatString,s,x,w))}}}z=null}function d(A,F,E){var B={indices:[],data:[]};var G,w,u,C,v,q,t;var z;var D=A.plugins.cursor;for(var w=0;w<A.series.length;w++){G=A.series[w];q=G.renderer;if(G.show){z=D.intersectionThreshold;if(G.showMarker){z+=G.markerRenderer.size/2}for(var v=0;v<G.gridData.length;v++){t=G.gridData[v];if(D.showVerticalLine){if(Math.abs(F-t[0])<=z){B.indices.push(w);B.data.push({seriesIndex:w,pointIndex:v,gridData:t,data:G.data[v]})}}}}}return B}function n(r,t){var v=t.plugins.cursor;var s=v._tooltipElem;switch(v.tooltipLocation){case"nw":var q=r.x+t._gridPadding.left-s.outerWidth(true)-v.tooltipOffset;var u=r.y+t._gridPadding.top-v.tooltipOffset-s.outerHeight(true);break;case"n":var q=r.x+t._gridPadding.left-s.outerWidth(true)/2;var u=r.y+t._gridPadding.top-v.tooltipOffset-s.outerHeight(true);break;case"ne":var q=r.x+t._gridPadding.left+v.tooltipOffset;var u=r.y+t._gridPadding.top-v.tooltipOffset-s.outerHeight(true);break;case"e":var q=r.x+t._gridPadding.left+v.tooltipOffset;var u=r.y+t._gridPadding.top-s.outerHeight(true)/2;break;case"se":var q=r.x+t._gridPadding.left+v.tooltipOffset;var u=r.y+t._gridPadding.top+v.tooltipOffset;break;case"s":var q=r.x+t._gridPadding.left-s.outerWidth(true)/2;var u=r.y+t._gridPadding.top+v.tooltipOffset;break;case"sw":var q=r.x+t._gridPadding.left-s.outerWidth(true)-v.tooltipOffset;var u=r.y+t._gridPadding.top+v.tooltipOffset;break;case"w":var q=r.x+t._gridPadding.left-s.outerWidth(true)-v.tooltipOffset;var u=r.y+t._gridPadding.top-s.outerHeight(true)/2;break;default:var q=r.x+t._gridPadding.left+v.tooltipOffset;var u=r.y+t._gridPadding.top+v.tooltipOffset;break}s.css("left",q);s.css("top",u);s=null}function m(u){var s=u._gridPadding;var v=u.plugins.cursor;var t=v._tooltipElem;switch(v.tooltipLocation){case"nw":var r=s.left+v.tooltipOffset;var q=s.top+v.tooltipOffset;t.css("left",r);t.css("top",q);break;case"n":var r=(s.left+(u._plotDimensions.width-s.right))/2-t.outerWidth(true)/2;var q=s.top+v.tooltipOffset;t.css("left",r);t.css("top",q);break;case"ne":var r=s.right+v.tooltipOffset;var q=s.top+v.tooltipOffset;t.css({right:r,top:q});break;case"e":var r=s.right+v.tooltipOffset;var q=(s.top+(u._plotDimensions.height-s.bottom))/2-t.outerHeight(true)/2;t.css({right:r,top:q});break;case"se":var r=s.right+v.tooltipOffset;var q=s.bottom+v.tooltipOffset;t.css({right:r,bottom:q});break;case"s":var r=(s.left+(u._plotDimensions.width-s.right))/2-t.outerWidth(true)/2;var q=s.bottom+v.tooltipOffset;t.css({left:r,bottom:q});break;case"sw":var r=s.left+v.tooltipOffset;var q=s.bottom+v.tooltipOffset;t.css({left:r,bottom:q});break;case"w":var r=s.left+v.tooltipOffset;var q=(s.top+(u._plotDimensions.height-s.bottom))/2-t.outerHeight(true)/2;t.css({left:r,top:q});break;default:var r=s.right-v.tooltipOffset;var q=s.bottom+v.tooltipOffset;t.css({right:r,bottom:q});break}t=null}function k(r,q,v,u,t){r.preventDefault();r.stopImmediatePropagation();var w=t.plugins.cursor;if(w.clickReset){w.resetZoom(t,w)}var s=window.getSelection;if(document.selection&&document.selection.empty){document.selection.empty()}else{if(s&&!s().isCollapsed){s().collapse()}}return false}function c(r,q,v,u,t){r.preventDefault();r.stopImmediatePropagation();var w=t.plugins.cursor;if(w.dblClickReset){w.resetZoom(t,w)}var s=window.getSelection;if(document.selection&&document.selection.empty){document.selection.empty()}else{if(s&&!s().isCollapsed){s().collapse()}}return false}function f(w,t,q,z,u){var v=u.plugins.cursor;v.onGrid=false;if(v.show){j(w.target).css("cursor",v.previousCursor);if(v.showTooltip&&!(v._zoom.zooming&&v.showTooltipOutsideZoom&&!v.constrainOutsideZoom)){v._tooltipElem.empty();v._tooltipElem.hide()}if(v.zoom){v._zoom.gridpos=t;v._zoom.datapos=q}if(v.showVerticalLine||v.showHorizontalLine){var B=v.cursorCanvas._ctx;B.clearRect(0,0,B.canvas.width,B.canvas.height);B=null}if(v.showCursorLegend){var A=j(u.targetId+" td.jqplot-cursor-legend-label");for(var s=0;s<A.length;s++){var y=j(A[s]).data("seriesIndex");var r=u.series[y];var x=r.label.toString();if(u.legend.escapeHtml){j(A[s]).text(j.jqplot.sprintf(v.cursorLegendFormatString,x,undefined,undefined))}else{j(A[s]).html(j.jqplot.sprintf(v.cursorLegendFormatString,x,undefined,undefined))}}}}}function b(r,q,u,t,s){var v=s.plugins.cursor;v.onGrid=true;if(v.show){v.previousCursor=r.target.style.cursor;r.target.style.cursor=v.style;if(v.showTooltip){e(q,u,s);if(v.followMouse){n(q,s)}else{m(s)}v._tooltipElem.show()}if(v.showVerticalLine||v.showHorizontalLine){g(q,s)}}}function i(r,q,u,t,s){var v=s.plugins.cursor;if(v.show){if(v.showTooltip){e(q,u,s);if(v.followMouse){n(q,s)}}if(v.showVerticalLine||v.showHorizontalLine){g(q,s)}}}function o(y){var x=y.data.plot;var t=x.eventCanvas._elem.offset();var w={x:y.pageX-t.left,y:y.pageY-t.top};var u={xaxis:null,yaxis:null,x2axis:null,y2axis:null,y3axis:null,y4axis:null,y5axis:null,y6axis:null,y7axis:null,y8axis:null,y9axis:null,yMidAxis:null};var v=["xaxis","yaxis","x2axis","y2axis","y3axis","y4axis","y5axis","y6axis","y7axis","y8axis","y9axis","yMidAxis"];var q=x.axes;var r,s;for(r=11;r>0;r--){s=v[r-1];if(q[s].show){u[s]=q[s].series_p2u(w[s.charAt(0)])}}return{offsets:t,gridPos:w,dataPos:u}}function h(z){var x=z.data.plot;var y=x.plugins.cursor;if(y.show&&y.zoom&&y._zoom.started&&!y.zoomTarget){z.preventDefault();var B=y.zoomCanvas._ctx;var v=o(z);var w=v.gridPos;var t=v.dataPos;y._zoom.gridpos=w;y._zoom.datapos=t;y._zoom.zooming=true;var u=w.x;var s=w.y;var A=B.canvas.height;var q=B.canvas.width;if(y.showTooltip&&!y.onGrid&&y.showTooltipOutsideZoom){e(w,t,x);if(y.followMouse){n(w,x)}}if(y.constrainZoomTo=="x"){y._zoom.end=[u,A]}else{if(y.constrainZoomTo=="y"){y._zoom.end=[q,s]}else{y._zoom.end=[u,s]}}var r=window.getSelection;if(document.selection&&document.selection.empty){document.selection.empty()}else{if(r&&!r().isCollapsed){r().collapse()}}l.call(y);B=null}}function a(w,s,r,x,t){var v=t.plugins.cursor;if(t.plugins.mobile){j(document).one("vmouseup.jqplot_cursor",{plot:t},p)}else{j(document).one("mouseup.jqplot_cursor",{plot:t},p)}var u=t.axes;if(document.onselectstart!=undefined){v._oldHandlers.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!=undefined){v._oldHandlers.ondrag=document.ondrag;document.ondrag=function(){return false}}if(document.onmousedown!=undefined){v._oldHandlers.onmousedown=document.onmousedown;document.onmousedown=function(){return false}}if(v.zoom){if(!v.zoomProxy){var y=v.zoomCanvas._ctx;y.clearRect(0,0,y.canvas.width,y.canvas.height);y=null}if(v.constrainZoomTo=="x"){v._zoom.start=[s.x,0]}else{if(v.constrainZoomTo=="y"){v._zoom.start=[0,s.y]}else{v._zoom.start=[s.x,s.y]}}v._zoom.started=true;for(var q in r){v._zoom.axes.start[q]=r[q]}if(t.plugins.mobile){j(document).bind("vmousemove.jqplotCursor",{plot:t},h)}else{j(document).bind("mousemove.jqplotCursor",{plot:t},h)}}}function p(y){var v=y.data.plot;var x=v.plugins.cursor;if(x.zoom&&x._zoom.zooming&&!x.zoomTarget){var u=x._zoom.gridpos.x;var r=x._zoom.gridpos.y;var t=x._zoom.datapos;var z=x.zoomCanvas._ctx.canvas.height;var q=x.zoomCanvas._ctx.canvas.width;var w=v.axes;if(x.constrainOutsideZoom&&!x.onGrid){if(u<0){u=0}else{if(u>q){u=q}}if(r<0){r=0}else{if(r>z){r=z}}for(var s in t){if(t[s]){if(s.charAt(0)=="x"){t[s]=w[s].series_p2u(u)}else{t[s]=w[s].series_p2u(r)}}}}if(x.constrainZoomTo=="x"){r=z}else{if(x.constrainZoomTo=="y"){u=q}}x._zoom.end=[u,r];x._zoom.gridpos={x:u,y:r};x.doZoom(x._zoom.gridpos,t,v,x)}x._zoom.started=false;x._zoom.zooming=false;j(document).unbind("mousemove.jqplotCursor",h);if(document.onselectstart!=undefined&&x._oldHandlers.onselectstart!=null){document.onselectstart=x._oldHandlers.onselectstart;x._oldHandlers.onselectstart=null}if(document.ondrag!=undefined&&x._oldHandlers.ondrag!=null){document.ondrag=x._oldHandlers.ondrag;x._oldHandlers.ondrag=null}if(document.onmousedown!=undefined&&x._oldHandlers.onmousedown!=null){document.onmousedown=x._oldHandlers.onmousedown;x._oldHandlers.onmousedown=null}}function l(){var y=this._zoom.start;var u=this._zoom.end;var s=this.zoomCanvas._ctx;var r,v,x,q;if(u[0]>y[0]){r=y[0];q=u[0]-y[0]}else{r=u[0];q=y[0]-u[0]}if(u[1]>y[1]){v=y[1];x=u[1]-y[1]}else{v=u[1];x=y[1]-u[1]}s.fillStyle="rgba(0,0,0,0.2)";s.strokeStyle="#999999";s.lineWidth=1;s.clearRect(0,0,s.canvas.width,s.canvas.height);s.fillRect(0,0,s.canvas.width,s.canvas.height);s.clearRect(r,v,q,x);s.strokeRect(r,v,q,x);s=null}j.jqplot.CursorLegendRenderer=function(q){j.jqplot.TableLegendRenderer.call(this,q);this.formatString="%s"};j.jqplot.CursorLegendRenderer.prototype=new j.jqplot.TableLegendRenderer();j.jqplot.CursorLegendRenderer.prototype.constructor=j.jqplot.CursorLegendRenderer;j.jqplot.CursorLegendRenderer.prototype.draw=function(){if(this._elem){this._elem.emptyForce();this._elem=null}if(this.show){var w=this._series,A;var r=document.createElement("table");this._elem=j(r);r=null;this._elem.addClass("jqplot-legend jqplot-cursor-legend");this._elem.css("position","absolute");var q=false;for(var x=0;x<w.length;x++){A=w[x];if(A.show&&A.showLabel){var v=j.jqplot.sprintf(this.formatString,A.label.toString());if(v){var t=A.color;if(A._stack&&!A.fill){t=""}z.call(this,v,t,q,x);q=true}for(var u=0;u<j.jqplot.addLegendRowHooks.length;u++){var y=j.jqplot.addLegendRowHooks[u].call(this,A);if(y){z.call(this,y.label,y.color,q);q=true}}}}w=A=null;delete w;delete A}function z(D,C,F,s){var B=(F)?this.rowSpacing:"0";var E=j('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem);E.data("seriesIndex",s);j('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+B+';"><div style="border:1px solid #cccccc;padding:0.2em;"><div class="jqplot-cursor-legend-swatch" style="background-color:'+C+';"></div></div></td>').appendTo(E);var G=j('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+B+';"></td>');G.appendTo(E);G.data("seriesIndex",s);if(this.escapeHtml){G.text(D)}else{G.html(D)}E=null;G=null}return this._elem}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js b/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js new file mode 100644 index 0000000..e371a28 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js @@ -0,0 +1,741 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.DateAxisRenderer + * A plugin for a jqPlot to render an axis as a series of date values. + * This renderer has no options beyond those supplied by the <Axis> class. + * It supplies its own tick formatter, so the tickOptions.formatter option + * should not be overridden. + * + * Thanks to Ken Synder for his enhanced Date instance methods which are + * included with this code <http://kendsnyder.com/sandbox/date/>. + * + * To use this renderer, include the plugin in your source + * > <script type="text/javascript" language="javascript" src="plugins/jqplot.dateAxisRenderer.js"></script> + * + * and supply the appropriate options to your plot + * + * > {axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}}} + * + * Dates can be passed into the axis in almost any recognizable value and + * will be parsed. They will be rendered on the axis in the format + * specified by tickOptions.formatString. e.g. tickOptions.formatString = '%Y-%m-%d'. + * + * Accecptable format codes + * are: + * + * > Code Result Description + * > == Years == + * > %Y 2008 Four-digit year + * > %y 08 Two-digit year + * > == Months == + * > %m 09 Two-digit month + * > %#m 9 One or two-digit month + * > %B September Full month name + * > %b Sep Abbreviated month name + * > == Days == + * > %d 05 Two-digit day of month + * > %#d 5 One or two-digit day of month + * > %e 5 One or two-digit day of month + * > %A Sunday Full name of the day of the week + * > %a Sun Abbreviated name of the day of the week + * > %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) + * > %o th The ordinal suffix string following the day of the month + * > == Hours == + * > %H 23 Hours in 24-hour format (two digits) + * > %#H 3 Hours in 24-hour integer format (one or two digits) + * > %I 11 Hours in 12-hour format (two digits) + * > %#I 3 Hours in 12-hour integer format (one or two digits) + * > %p PM AM or PM + * > == Minutes == + * > %M 09 Minutes (two digits) + * > %#M 9 Minutes (one or two digits) + * > == Seconds == + * > %S 02 Seconds (two digits) + * > %#S 2 Seconds (one or two digits) + * > %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) + * > == Milliseconds == + * > %N 008 Milliseconds (three digits) + * > %#N 8 Milliseconds (one to three digits) + * > == Timezone == + * > %O 360 difference in minutes between local time and GMT + * > %Z Mountain Standard Time Name of timezone as reported by browser + * > %G -06:00 Hours and minutes between GMT + * > == Shortcuts == + * > %F 2008-03-26 %Y-%m-%d + * > %T 05:06:30 %H:%M:%S + * > %X 05:06:30 %H:%M:%S + * > %x 03/26/08 %m/%d/%y + * > %D 03/26/08 %m/%d/%y + * > %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y + * > %v 3-Sep-2008 %e-%b-%Y + * > %R 15:31 %H:%M + * > %r 3:31:00 PM %I:%M:%S %p + * > == Characters == + * > %n \n Newline + * > %t \t Tab + * > %% % Percent Symbol + */ + $.jqplot.DateAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + this.date = new $.jsDate(); + }; + + var second = 1000; + var minute = 60 * second; + var hour = 60 * minute; + var day = 24 * hour; + var week = 7 * day; + + // these are less definitive + var month = 30.4368499 * day; + var year = 365.242199 * day; + + var daysInMonths = [31,28,31,30,31,30,31,30,31,30,31,30]; + // array of consistent nice intervals. Longer intervals + // will depend on days in month, days in year, etc. + var niceFormatStrings = ['%M:%S.%#N', '%M:%S.%#N', '%M:%S.%#N', '%M:%S', '%M:%S', '%M:%S', '%M:%S', '%H:%M:%S', '%H:%M:%S', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%a %H:%M', '%a %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%v', '%v', '%v', '%v', '%v', '%v', '%v']; + var niceIntervals = [0.1*second, 0.2*second, 0.5*second, second, 2*second, 5*second, 10*second, 15*second, 30*second, minute, 2*minute, 5*minute, 10*minute, 15*minute, 30*minute, hour, 2*hour, 4*hour, 6*hour, 8*hour, 12*hour, day, 2*day, 3*day, 4*day, 5*day, week, 2*week]; + + var niceMonthlyIntervals = []; + + function bestDateInterval(min, max, titarget) { + // iterate through niceIntervals to find one closest to titarget + var badness = Number.MAX_VALUE; + var temp, bestTi, bestfmt; + for (var i=0, l=niceIntervals.length; i < l; i++) { + temp = Math.abs(titarget - niceIntervals[i]); + if (temp < badness) { + badness = temp; + bestTi = niceIntervals[i]; + bestfmt = niceFormatStrings[i]; + } + } + + return [bestTi, bestfmt]; + } + + $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.DateAxisRenderer.prototype.constructor = $.jqplot.DateAxisRenderer; + + $.jqplot.DateTickFormatter = function(format, val) { + if (!format) { + format = '%Y/%m/%d'; + } + return $.jsDate.strftime(val, format); + }; + + $.jqplot.DateAxisRenderer.prototype.init = function(options){ + // prop: tickRenderer + // A class of a rendering engine for creating the ticks labels displayed on the plot, + // See <$.jqplot.AxisTickRenderer>. + // this.tickRenderer = $.jqplot.AxisTickRenderer; + // this.labelRenderer = $.jqplot.AxisLabelRenderer; + this.tickOptions.formatter = $.jqplot.DateTickFormatter; + // prop: tickInset + // Controls the amount to inset the first and last ticks from + // the edges of the grid, in multiples of the tick interval. + // 0 is no inset, 0.5 is one half a tick interval, 1 is a full + // tick interval, etc. + this.tickInset = 0; + // prop: drawBaseline + // True to draw the axis baseline. + this.drawBaseline = true; + // prop: baselineWidth + // width of the baseline in pixels. + this.baselineWidth = null; + // prop: baselineColor + // CSS color spec for the baseline. + this.baselineColor = null; + this.daTickInterval = null; + this._daTickInterval = null; + + $.extend(true, this, options); + + var db = this._dataBounds, + stats, + sum, + s, + d, + pd, + sd, + intv; + + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + for (var i=0; i<this._series.length; i++) { + stats = {intervals:[], frequencies:{}, sortedIntervals:[], min:null, max:null, mean:null}; + sum = 0; + s = this._series[i]; + d = s.data; + pd = s._plotData; + sd = s._stackData; + intv = 0; + + for (var j=0; j<d.length; j++) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + d[j][0] = new $.jsDate(d[j][0]).getTime(); + pd[j][0] = new $.jsDate(d[j][0]).getTime(); + sd[j][0] = new $.jsDate(d[j][0]).getTime(); + if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { + db.min = d[j][0]; + } + if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { + db.max = d[j][0]; + } + if (j>0) { + intv = Math.abs(d[j][0] - d[j-1][0]); + stats.intervals.push(intv); + if (stats.frequencies.hasOwnProperty(intv)) { + stats.frequencies[intv] += 1; + } + else { + stats.frequencies[intv] = 1; + } + } + sum += intv; + + } + else { + d[j][1] = new $.jsDate(d[j][1]).getTime(); + pd[j][1] = new $.jsDate(d[j][1]).getTime(); + sd[j][1] = new $.jsDate(d[j][1]).getTime(); + if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) { + db.min = d[j][1]; + } + if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) { + db.max = d[j][1]; + } + if (j>0) { + intv = Math.abs(d[j][1] - d[j-1][1]); + stats.intervals.push(intv); + if (stats.frequencies.hasOwnProperty(intv)) { + stats.frequencies[intv] += 1; + } + else { + stats.frequencies[intv] = 1; + } + } + } + sum += intv; + } + + if (s.renderer.bands) { + if (s.renderer.bands.hiData.length) { + var bd = s.renderer.bands.hiData; + for (var j=0, l=bd.length; j < l; j++) { + if (this.name === 'xaxis' || this.name === 'x2axis') { + bd[j][0] = new $.jsDate(bd[j][0]).getTime(); + if ((bd[j][0] != null && bd[j][0] > db.max) || db.max == null) { + db.max = bd[j][0]; + } + } + else { + bd[j][1] = new $.jsDate(bd[j][1]).getTime(); + if ((bd[j][1] != null && bd[j][1] > db.max) || db.max == null) { + db.max = bd[j][1]; + } + } + } + } + if (s.renderer.bands.lowData.length) { + var bd = s.renderer.bands.lowData; + for (var j=0, l=bd.length; j < l; j++) { + if (this.name === 'xaxis' || this.name === 'x2axis') { + bd[j][0] = new $.jsDate(bd[j][0]).getTime(); + if ((bd[j][0] != null && bd[j][0] < db.min) || db.min == null) { + db.min = bd[j][0]; + } + } + else { + bd[j][1] = new $.jsDate(bd[j][1]).getTime(); + if ((bd[j][1] != null && bd[j][1] < db.min) || db.min == null) { + db.min = bd[j][1]; + } + } + } + } + } + + var tempf = 0, + tempn=0; + for (var n in stats.frequencies) { + stats.sortedIntervals.push({interval:n, frequency:stats.frequencies[n]}); + } + stats.sortedIntervals.sort(function(a, b){ + return b.frequency - a.frequency; + }); + + stats.min = $.jqplot.arrayMin(stats.intervals); + stats.max = $.jqplot.arrayMax(stats.intervals); + stats.mean = sum/d.length; + this._intervalStats.push(stats); + stats = sum = s = d = pd = sd = null; + } + db = null; + + }; + + // called with scope of an axis + $.jqplot.DateAxisRenderer.prototype.reset = function() { + this.min = this._options.min; + this.max = this._options.max; + this.tickInterval = this._options.tickInterval; + this.numberTicks = this._options.numberTicks; + this._autoFormatString = ''; + if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { + this.tickOptions.formatString = ''; + } + this.daTickInterval = this._daTickInterval; + // this._ticks = this.__ticks; + }; + + $.jqplot.DateAxisRenderer.prototype.createTicks = function(plot) { + // we're are operating on an axis here + var ticks = this._ticks; + var userTicks = this.ticks; + var name = this.name; + // databounds were set on axis initialization. + var db = this._dataBounds; + var iv = this._intervalStats; + var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; + var interval; + var min, max; + var pos1, pos2; + var tt, i; + var threshold = 30; + var insetMult = 1; + var daTickInterval = null; + + // if user specified a tick interval, convert to usable. + if (this.tickInterval != null) + { + // if interval is a number or can be converted to one, use it. + // Assume it is in SECONDS!!! + if (Number(this.tickInterval)) { + daTickInterval = [Number(this.tickInterval), 'seconds']; + } + // else, parse out something we can build from. + else if (typeof this.tickInterval == "string") { + var parts = this.tickInterval.split(' '); + if (parts.length == 1) { + daTickInterval = [1, parts[0]]; + } + else if (parts.length == 2) { + daTickInterval = [parts[0], parts[1]]; + } + } + } + + var tickInterval = this.tickInterval; + + // if we already have ticks, use them. + // ticks must be in order of increasing value. + + min = new $.jsDate((this.min != null) ? this.min : db.min).getTime(); + max = new $.jsDate((this.max != null) ? this.max : db.max).getTime(); + + // see if we're zooming. if we are, don't use the min and max we're given, + // but compute some nice ones. They will be reset later. + + var cursor = plot.plugins.cursor; + + if (cursor && cursor._zoom && cursor._zoom.zooming) { + this.min = null; + this.max = null; + } + + var range = max - min; + + if (this.tickOptions == null || !this.tickOptions.formatString) { + this._overrideFormatString = true; + } + + if (userTicks.length) { + // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed + for (i=0; i<userTicks.length; i++){ + var ut = userTicks[i]; + var t = new this.tickRenderer(this.tickOptions); + if (ut.constructor == Array) { + t.value = new $.jsDate(ut[0]).getTime(); + t.label = ut[1]; + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(t.value, this.name); + this._ticks.push(t); + } + + else { + t.value = new $.jsDate(ut).getTime(); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(t.value, this.name); + this._ticks.push(t); + } + } + this.numberTicks = userTicks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this.numberTicks-1].value; + this.daTickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds']; + } + + //////// + // We don't have any ticks yet, let's make some! + //////// + + // special case when there is only one point, make three tick marks to center the point + else if (this.min == null && this.max == null && db.min == db.max) + { + var onePointOpts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); + var delta = 300000; + this.min = db.min - delta; + this.max = db.max + delta; + this.numberTicks = 3; + + for(var i=this.min;i<=this.max;i+= delta) + { + onePointOpts.value = i; + + var t = new this.tickRenderer(onePointOpts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + + t.showLabel = false; + t.showMark = false; + + this._ticks.push(t); + } + + if(this.showTicks) { + this._ticks[1].showLabel = true; + } + if(this.showTickMarks) { + this._ticks[1].showTickMarks = true; + } + } + // if user specified min and max are null, we set those to make best ticks. + else if (this.min == null && this.max == null) { + + var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); + + // want to find a nice interval + var nttarget, + titarget; + + // if no tickInterval or numberTicks options specified, make a good guess. + if (!this.tickInterval && !this.numberTicks) { + var tdim = Math.max(dim, threshold+1); + // how many ticks to put on the axis? + // date labels tend to be long. If ticks not rotated, + // don't use too many and have a high spacing factor. + // If we are rotating ticks, use a lower factor. + var spacingFactor = 115; + if (this.tickRenderer === $.jqplot.CanvasAxisTickRenderer && this.tickOptions.angle) { + spacingFactor = 115 - 40 * Math.abs(Math.sin(this.tickOptions.angle/180*Math.PI)); + } + + nttarget = Math.ceil((tdim-threshold)/spacingFactor + 1); + titarget = (max - min) / (nttarget - 1); + } + + // If tickInterval is specified, we'll try to honor it. + // Not guaranteed to get this interval, but we'll get as close as + // we can. + // tickInterval will be used before numberTicks, that is if + // both are specified, numberTicks will be ignored. + else if (this.tickInterval) { + titarget = new $.jsDate(0).add(daTickInterval[0], daTickInterval[1]).getTime(); + } + + // if numberTicks specified, try to honor it. + // Not guaranteed, but will try to get close. + else if (this.numberTicks) { + nttarget = this.numberTicks; + titarget = (max - min) / (nttarget - 1); + } + + // If we can use an interval of 2 weeks or less, pick best one + if (titarget <= 19*day) { + var ret = bestDateInterval(min, max, titarget); + var tempti = ret[0]; + this._autoFormatString = ret[1]; + + min = new $.jsDate(min); + min = Math.floor((min.getTime() - min.getUtcOffset())/tempti) * tempti + min.getUtcOffset(); + + nttarget = Math.ceil((max - min) / tempti) + 1; + this.min = min; + this.max = min + (nttarget - 1) * tempti; + + // if max is less than max, add an interval + if (this.max < max) { + this.max += tempti; + nttarget += 1; + } + this.tickInterval = tempti; + this.numberTicks = nttarget; + + for (var i=0; i<nttarget; i++) { + opts.value = this.min + i * tempti; + t = new this.tickRenderer(opts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + this._ticks.push(t); + } + + insetMult = this.tickInterval; + } + + // should we use a monthly interval? + else if (titarget <= 9 * month) { + + this._autoFormatString = '%v'; + + // how many months in an interval? + var intv = Math.round(titarget/month); + if (intv < 1) { + intv = 1; + } + else if (intv > 6) { + intv = 6; + } + + // figure out the starting month and ending month. + var mstart = new $.jsDate(min).setDate(1).setHours(0,0,0,0); + + // See if max ends exactly on a month + var tempmend = new $.jsDate(max); + var mend = new $.jsDate(max).setDate(1).setHours(0,0,0,0); + + if (tempmend.getTime() !== mend.getTime()) { + mend = mend.add(1, 'month'); + } + + var nmonths = mend.diff(mstart, 'month'); + + nttarget = Math.ceil(nmonths/intv) + 1; + + this.min = mstart.getTime(); + this.max = mstart.clone().add((nttarget - 1) * intv, 'month').getTime(); + this.numberTicks = nttarget; + + for (var i=0; i<nttarget; i++) { + if (i === 0) { + opts.value = mstart.getTime(); + } + else { + opts.value = mstart.add(intv, 'month').getTime(); + } + t = new this.tickRenderer(opts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + this._ticks.push(t); + } + + insetMult = intv * month; + } + + // use yearly intervals + else { + + this._autoFormatString = '%v'; + + // how many years in an interval? + var intv = Math.round(titarget/year); + if (intv < 1) { + intv = 1; + } + + // figure out the starting and ending years. + var mstart = new $.jsDate(min).setMonth(0, 1).setHours(0,0,0,0); + var mend = new $.jsDate(max).add(1, 'year').setMonth(0, 1).setHours(0,0,0,0); + + var nyears = mend.diff(mstart, 'year'); + + nttarget = Math.ceil(nyears/intv) + 1; + + this.min = mstart.getTime(); + this.max = mstart.clone().add((nttarget - 1) * intv, 'year').getTime(); + this.numberTicks = nttarget; + + for (var i=0; i<nttarget; i++) { + if (i === 0) { + opts.value = mstart.getTime(); + } + else { + opts.value = mstart.add(intv, 'year').getTime(); + } + t = new this.tickRenderer(opts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + this._ticks.push(t); + } + + insetMult = intv * year; + } + } + + //////// + // Some option(s) specified, work around that. + //////// + + else { + if (name == 'xaxis' || name == 'x2axis') { + dim = this._plotDimensions.width; + } + else { + dim = this._plotDimensions.height; + } + + // if min, max and number of ticks specified, user can't specify interval. + if (this.min != null && this.max != null && this.numberTicks != null) { + this.tickInterval = null; + } + + if (this.tickInterval != null && daTickInterval != null) { + this.daTickInterval = daTickInterval; + } + + // if min and max are same, space them out a bit + if (min == max) { + var adj = 24*60*60*500; // 1/2 day + min -= adj; + max += adj; + } + + range = max - min; + + var optNumTicks = 2 + parseInt(Math.max(0, dim-100)/100, 10); + + + var rmin, rmax; + + rmin = (this.min != null) ? new $.jsDate(this.min).getTime() : min - range/2*(this.padMin - 1); + rmax = (this.max != null) ? new $.jsDate(this.max).getTime() : max + range/2*(this.padMax - 1); + this.min = rmin; + this.max = rmax; + range = this.max - this.min; + + if (this.numberTicks == null){ + // if tickInterval is specified by user, we will ignore computed maximum. + // max will be equal or greater to fit even # of ticks. + if (this.daTickInterval != null) { + var nc = new $.jsDate(this.max).diff(this.min, this.daTickInterval[1], true); + this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1; + // this.max = new $.jsDate(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime(); + this.max = new $.jsDate(this.min).add((this.numberTicks-1) * this.daTickInterval[0], this.daTickInterval[1]).getTime(); + } + else if (dim > 200) { + this.numberTicks = parseInt(3+(dim-200)/100, 10); + } + else { + this.numberTicks = 2; + } + } + + insetMult = range / (this.numberTicks-1)/1000; + + if (this.daTickInterval == null) { + this.daTickInterval = [insetMult, 'seconds']; + } + + + for (var i=0; i<this.numberTicks; i++){ + var min = new $.jsDate(this.min); + tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime(); + var t = new this.tickRenderer(this.tickOptions); + // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(tt, this.name); + this._ticks.push(t); + } + } + + if (this.tickInset) { + this.min = this.min - this.tickInset * insetMult; + this.max = this.max + this.tickInset * insetMult; + } + + if (this._daTickInterval == null) { + this._daTickInterval = this.daTickInterval; + } + + ticks = null; + }; + +})(jQuery); + diff --git a/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.min.js new file mode 100644 index 0000000..7417801 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.dateAxisRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(h){h.jqplot.DateAxisRenderer=function(){h.jqplot.LinearAxisRenderer.call(this);this.date=new h.jsDate()};var c=1000;var e=60*c;var f=60*e;var l=24*f;var b=7*l;var j=30.4368499*l;var k=365.242199*l;var g=[31,28,31,30,31,30,31,30,31,30,31,30];var i=["%M:%S.%#N","%M:%S.%#N","%M:%S.%#N","%M:%S","%M:%S","%M:%S","%M:%S","%H:%M:%S","%H:%M:%S","%H:%M","%H:%M","%H:%M","%H:%M","%H:%M","%H:%M","%a %H:%M","%a %H:%M","%b %e %H:%M","%b %e %H:%M","%b %e %H:%M","%b %e %H:%M","%v","%v","%v","%v","%v","%v","%v"];var m=[0.1*c,0.2*c,0.5*c,c,2*c,5*c,10*c,15*c,30*c,e,2*e,5*e,10*e,15*e,30*e,f,2*f,4*f,6*f,8*f,12*f,l,2*l,3*l,4*l,5*l,b,2*b];var d=[];function a(p,s,t){var o=Number.MAX_VALUE;var u,r,v;for(var q=0,n=m.length;q<n;q++){u=Math.abs(t-m[q]);if(u<o){o=u;r=m[q];v=i[q]}}return[r,v]}h.jqplot.DateAxisRenderer.prototype=new h.jqplot.LinearAxisRenderer();h.jqplot.DateAxisRenderer.prototype.constructor=h.jqplot.DateAxisRenderer;h.jqplot.DateTickFormatter=function(n,o){if(!n){n="%Y/%m/%d"}return h.jsDate.strftime(o,n)};h.jqplot.DateAxisRenderer.prototype.init=function(E){this.tickOptions.formatter=h.jqplot.DateTickFormatter;this.tickInset=0;this.drawBaseline=true;this.baselineWidth=null;this.baselineColor=null;this.daTickInterval=null;this._daTickInterval=null;h.extend(true,this,E);var C=this._dataBounds,u,x,D,y,A,z,o;for(var t=0;t<this._series.length;t++){u={intervals:[],frequencies:{},sortedIntervals:[],min:null,max:null,mean:null};x=0;D=this._series[t];y=D.data;A=D._plotData;z=D._stackData;o=0;for(var r=0;r<y.length;r++){if(this.name=="xaxis"||this.name=="x2axis"){y[r][0]=new h.jsDate(y[r][0]).getTime();A[r][0]=new h.jsDate(y[r][0]).getTime();z[r][0]=new h.jsDate(y[r][0]).getTime();if((y[r][0]!=null&&y[r][0]<C.min)||C.min==null){C.min=y[r][0]}if((y[r][0]!=null&&y[r][0]>C.max)||C.max==null){C.max=y[r][0]}if(r>0){o=Math.abs(y[r][0]-y[r-1][0]);u.intervals.push(o);if(u.frequencies.hasOwnProperty(o)){u.frequencies[o]+=1}else{u.frequencies[o]=1}}x+=o}else{y[r][1]=new h.jsDate(y[r][1]).getTime();A[r][1]=new h.jsDate(y[r][1]).getTime();z[r][1]=new h.jsDate(y[r][1]).getTime();if((y[r][1]!=null&&y[r][1]<C.min)||C.min==null){C.min=y[r][1]}if((y[r][1]!=null&&y[r][1]>C.max)||C.max==null){C.max=y[r][1]}if(r>0){o=Math.abs(y[r][1]-y[r-1][1]);u.intervals.push(o);if(u.frequencies.hasOwnProperty(o)){u.frequencies[o]+=1}else{u.frequencies[o]=1}}}x+=o}if(D.renderer.bands){if(D.renderer.bands.hiData.length){var w=D.renderer.bands.hiData;for(var r=0,q=w.length;r<q;r++){if(this.name==="xaxis"||this.name==="x2axis"){w[r][0]=new h.jsDate(w[r][0]).getTime();if((w[r][0]!=null&&w[r][0]>C.max)||C.max==null){C.max=w[r][0]}}else{w[r][1]=new h.jsDate(w[r][1]).getTime();if((w[r][1]!=null&&w[r][1]>C.max)||C.max==null){C.max=w[r][1]}}}}if(D.renderer.bands.lowData.length){var w=D.renderer.bands.lowData;for(var r=0,q=w.length;r<q;r++){if(this.name==="xaxis"||this.name==="x2axis"){w[r][0]=new h.jsDate(w[r][0]).getTime();if((w[r][0]!=null&&w[r][0]<C.min)||C.min==null){C.min=w[r][0]}}else{w[r][1]=new h.jsDate(w[r][1]).getTime();if((w[r][1]!=null&&w[r][1]<C.min)||C.min==null){C.min=w[r][1]}}}}}var B=0,v=0;for(var p in u.frequencies){u.sortedIntervals.push({interval:p,frequency:u.frequencies[p]})}u.sortedIntervals.sort(function(s,n){return n.frequency-s.frequency});u.min=h.jqplot.arrayMin(u.intervals);u.max=h.jqplot.arrayMax(u.intervals);u.mean=x/y.length;this._intervalStats.push(u);u=x=D=y=A=z=null}C=null};h.jqplot.DateAxisRenderer.prototype.reset=function(){this.min=this._options.min;this.max=this._options.max;this.tickInterval=this._options.tickInterval;this.numberTicks=this._options.numberTicks;this._autoFormatString="";if(this._overrideFormatString&&this.tickOptions&&this.tickOptions.formatString){this.tickOptions.formatString=""}this.daTickInterval=this._daTickInterval};h.jqplot.DateAxisRenderer.prototype.createTicks=function(p){var X=this._ticks;var L=this.ticks;var F=this.name;var H=this._dataBounds;var M=this._intervalStats;var n=(this.name.charAt(0)==="x")?this._plotDimensions.width:this._plotDimensions.height;var w;var ae,J;var y,x;var ad,aa;var s=30;var O=1;var U=null;if(this.tickInterval!=null){if(Number(this.tickInterval)){U=[Number(this.tickInterval),"seconds"]}else{if(typeof this.tickInterval=="string"){var ac=this.tickInterval.split(" ");if(ac.length==1){U=[1,ac[0]]}else{if(ac.length==2){U=[ac[0],ac[1]]}}}}}var v=this.tickInterval;ae=new h.jsDate((this.min!=null)?this.min:H.min).getTime();J=new h.jsDate((this.max!=null)?this.max:H.max).getTime();var A=p.plugins.cursor;if(A&&A._zoom&&A._zoom.zooming){this.min=null;this.max=null}var B=J-ae;if(this.tickOptions==null||!this.tickOptions.formatString){this._overrideFormatString=true}if(L.length){for(aa=0;aa<L.length;aa++){var P=L[aa];var Y=new this.tickRenderer(this.tickOptions);if(P.constructor==Array){Y.value=new h.jsDate(P[0]).getTime();Y.label=P[1];if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}Y.setTick(Y.value,this.name);this._ticks.push(Y)}else{Y.value=new h.jsDate(P).getTime();if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}Y.setTick(Y.value,this.name);this._ticks.push(Y)}}this.numberTicks=L.length;this.min=this._ticks[0].value;this.max=this._ticks[this.numberTicks-1].value;this.daTickInterval=[(this.max-this.min)/(this.numberTicks-1)/1000,"seconds"]}else{if(this.min==null&&this.max==null&&H.min==H.max){var E=h.extend(true,{},this.tickOptions,{name:this.name,value:null});var T=300000;this.min=H.min-T;this.max=H.max+T;this.numberTicks=3;for(var aa=this.min;aa<=this.max;aa+=T){E.value=aa;var Y=new this.tickRenderer(E);if(this._overrideFormatString&&this._autoFormatString!=""){Y.formatString=this._autoFormatString}Y.showLabel=false;Y.showMark=false;this._ticks.push(Y)}if(this.showTicks){this._ticks[1].showLabel=true}if(this.showTickMarks){this._ticks[1].showTickMarks=true}}else{if(this.min==null&&this.max==null){var N=h.extend(true,{},this.tickOptions,{name:this.name,value:null});var ab,I;if(!this.tickInterval&&!this.numberTicks){var R=Math.max(n,s+1);var Z=115;if(this.tickRenderer===h.jqplot.CanvasAxisTickRenderer&&this.tickOptions.angle){Z=115-40*Math.abs(Math.sin(this.tickOptions.angle/180*Math.PI))}ab=Math.ceil((R-s)/Z+1);I=(J-ae)/(ab-1)}else{if(this.tickInterval){I=new h.jsDate(0).add(U[0],U[1]).getTime()}else{if(this.numberTicks){ab=this.numberTicks;I=(J-ae)/(ab-1)}}}if(I<=19*l){var Q=a(ae,J,I);var r=Q[0];this._autoFormatString=Q[1];ae=new h.jsDate(ae);ae=Math.floor((ae.getTime()-ae.getUtcOffset())/r)*r+ae.getUtcOffset();ab=Math.ceil((J-ae)/r)+1;this.min=ae;this.max=ae+(ab-1)*r;if(this.max<J){this.max+=r;ab+=1}this.tickInterval=r;this.numberTicks=ab;for(var aa=0;aa<ab;aa++){N.value=this.min+aa*r;Y=new this.tickRenderer(N);if(this._overrideFormatString&&this._autoFormatString!=""){Y.formatString=this._autoFormatString}if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}this._ticks.push(Y)}O=this.tickInterval}else{if(I<=9*j){this._autoFormatString="%v";var D=Math.round(I/j);if(D<1){D=1}else{if(D>6){D=6}}var V=new h.jsDate(ae).setDate(1).setHours(0,0,0,0);var q=new h.jsDate(J);var z=new h.jsDate(J).setDate(1).setHours(0,0,0,0);if(q.getTime()!==z.getTime()){z=z.add(1,"month")}var S=z.diff(V,"month");ab=Math.ceil(S/D)+1;this.min=V.getTime();this.max=V.clone().add((ab-1)*D,"month").getTime();this.numberTicks=ab;for(var aa=0;aa<ab;aa++){if(aa===0){N.value=V.getTime()}else{N.value=V.add(D,"month").getTime()}Y=new this.tickRenderer(N);if(this._overrideFormatString&&this._autoFormatString!=""){Y.formatString=this._autoFormatString}if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}this._ticks.push(Y)}O=D*j}else{this._autoFormatString="%v";var D=Math.round(I/k);if(D<1){D=1}var V=new h.jsDate(ae).setMonth(0,1).setHours(0,0,0,0);var z=new h.jsDate(J).add(1,"year").setMonth(0,1).setHours(0,0,0,0);var K=z.diff(V,"year");ab=Math.ceil(K/D)+1;this.min=V.getTime();this.max=V.clone().add((ab-1)*D,"year").getTime();this.numberTicks=ab;for(var aa=0;aa<ab;aa++){if(aa===0){N.value=V.getTime()}else{N.value=V.add(D,"year").getTime()}Y=new this.tickRenderer(N);if(this._overrideFormatString&&this._autoFormatString!=""){Y.formatString=this._autoFormatString}if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}this._ticks.push(Y)}O=D*k}}}else{if(F=="xaxis"||F=="x2axis"){n=this._plotDimensions.width}else{n=this._plotDimensions.height}if(this.min!=null&&this.max!=null&&this.numberTicks!=null){this.tickInterval=null}if(this.tickInterval!=null&&U!=null){this.daTickInterval=U}if(ae==J){var o=24*60*60*500;ae-=o;J+=o}B=J-ae;var G=2+parseInt(Math.max(0,n-100)/100,10);var W,C;W=(this.min!=null)?new h.jsDate(this.min).getTime():ae-B/2*(this.padMin-1);C=(this.max!=null)?new h.jsDate(this.max).getTime():J+B/2*(this.padMax-1);this.min=W;this.max=C;B=this.max-this.min;if(this.numberTicks==null){if(this.daTickInterval!=null){var u=new h.jsDate(this.max).diff(this.min,this.daTickInterval[1],true);this.numberTicks=Math.ceil(u/this.daTickInterval[0])+1;this.max=new h.jsDate(this.min).add((this.numberTicks-1)*this.daTickInterval[0],this.daTickInterval[1]).getTime()}else{if(n>200){this.numberTicks=parseInt(3+(n-200)/100,10)}else{this.numberTicks=2}}}O=B/(this.numberTicks-1)/1000;if(this.daTickInterval==null){this.daTickInterval=[O,"seconds"]}for(var aa=0;aa<this.numberTicks;aa++){var ae=new h.jsDate(this.min);ad=ae.add(aa*this.daTickInterval[0],this.daTickInterval[1]).getTime();var Y=new this.tickRenderer(this.tickOptions);if(!this.showTicks){Y.showLabel=false;Y.showMark=false}else{if(!this.showTickMarks){Y.showMark=false}}Y.setTick(ad,this.name);this._ticks.push(Y)}}}}if(this.tickInset){this.min=this.min-this.tickInset*O;this.max=this.max+this.tickInset*O}if(this._daTickInterval==null){this._daTickInterval=this.daTickInterval}X=null}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.donutRenderer.js b/web/extensions/jqplot/plugins/jqplot.donutRenderer.js new file mode 100644 index 0000000..9684812 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.donutRenderer.js @@ -0,0 +1,805 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.DonutRenderer + * Plugin renderer to draw a donut chart. + * x values, if present, will be used as slice labels. + * y values give slice size. + * + * To use this renderer, you need to include the + * donut renderer plugin, for example: + * + * > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script> + * + * Properties described here are passed into the $.jqplot function + * as options on the series renderer. For example: + * + * > plot2 = $.jqplot('chart2', [s1, s2], { + * > seriesDefaults: { + * > renderer:$.jqplot.DonutRenderer, + * > rendererOptions:{ + * > sliceMargin: 2, + * > innerDiameter: 110, + * > startAngle: -90 + * > } + * > } + * > }); + * + * A donut plot will trigger events on the plot target + * according to user interaction. All events return the event object, + * the series index, the point (slice) index, and the point data for + * the appropriate slice. + * + * 'jqplotDataMouseOver' - triggered when user mouseing over a slice. + * 'jqplotDataHighlight' - triggered the first time user mouses over a slice, + * if highlighting is enabled. + * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of + * a highlighted slice. + * 'jqplotDataClick' - triggered when the user clicks on a slice. + * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if + * the "captureRightClick" option is set to true on the plot. + */ + $.jqplot.DonutRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer; + + // called with scope of a series + $.jqplot.DonutRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + // prop: diameter + // Outer diameter of the donut, auto computed by default + this.diameter = null; + // prop: innerDiameter + // Inner diameter of the donut, auto calculated by default. + // If specified will override thickness value. + this.innerDiameter = null; + // prop: thickness + // thickness of the donut, auto computed by default + // Overridden by if innerDiameter is specified. + this.thickness = null; + // prop: padding + // padding between the donut and plot edges, legend, etc. + this.padding = 20; + // prop: sliceMargin + // angular spacing between donut slices in degrees. + this.sliceMargin = 0; + // prop: ringMargin + // pixel distance between rings, or multiple series in a donut plot. + // null will compute ringMargin based on sliceMargin. + this.ringMargin = null; + // prop: fill + // true or false, whether to fil the slices. + this.fill = true; + // prop: shadowOffset + // offset of the shadow from the slice and offset of + // each succesive stroke of the shadow from the last. + this.shadowOffset = 2; + // prop: shadowAlpha + // transparency of the shadow (0 = transparent, 1 = opaque) + this.shadowAlpha = 0.07; + // prop: shadowDepth + // number of strokes to apply to the shadow, + // each stroke offset shadowOffset from the last. + this.shadowDepth = 5; + // prop: highlightMouseOver + // True to highlight slice when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a slice. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a slice. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // an array of colors to use when highlighting a slice. + this.highlightColors = []; + // prop: dataLabels + // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. + // Defaults to percentage of each pie slice. + this.dataLabels = 'percent'; + // prop: showDataLabels + // true to show data labels on slices. + this.showDataLabels = false; + // prop: dataLabelFormatString + // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. + this.dataLabelFormatString = null; + // prop: dataLabelThreshold + // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. + // This applies to all label types, not just to percentage labels. + this.dataLabelThreshold = 3; + // prop: dataLabelPositionFactor + // A Multiplier (0-1) of the pie radius which controls position of label on slice. + // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. + this.dataLabelPositionFactor = 0.4; + // prop: dataLabelNudge + // Number of pixels to slide the label away from (+) or toward (-) the center of the pie. + this.dataLabelNudge = 0; + // prop: startAngle + // Angle to start drawing donut in degrees. + // According to orientation of canvas coordinate system: + // 0 = on the positive x axis + // -90 = on the positive y axis. + // 90 = on the negaive y axis. + // 180 or - 180 = on the negative x axis. + this.startAngle = 0; + this.tickRenderer = $.jqplot.DonutTickRenderer; + // Used as check for conditions where donut shouldn't be drawn. + this._drawData = true; + this._type = 'donut'; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + $.extend(true, this, options); + if (this.diameter != null) { + this.diameter = this.diameter - this.sliceMargin; + } + this._diameter = null; + this._innerDiameter = null; + this._radius = null; + this._innerRadius = null; + this._thickness = null; + // references to the previous series in the plot to properly calculate diameters + // and thicknesses of nested rings. + this._previousSeries = []; + this._numberSeries = 1; + // array of [start,end] angles arrays, one for each slice. In radians. + this._sliceAngles = []; + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + + // set highlight colors if none provided + if (this.highlightColors.length == 0) { + for (var i=0; i<this.seriesColors.length; i++){ + var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); + } + } + + plot.postParseOptionsHooks.addOnce(postParseOptions); + plot.postInitHooks.addOnce(postInit); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + plot.postDrawHooks.addOnce(postPlotDraw); + + + }; + + $.jqplot.DonutRenderer.prototype.setGridData = function(plot) { + // set gridData property. This will hold angle in radians of each data point. + var stack = []; + var td = []; + var sa = this.startAngle/180*Math.PI; + var tot = 0; + // don't know if we have any valid data yet, so set plot to not draw. + this._drawData = false; + for (var i=0; i<this.data.length; i++){ + if (this.data[i][1] != 0) { + // we have data, O.K. to draw. + this._drawData = true; + } + stack.push(this.data[i][1]); + td.push([this.data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + tot += this.data[i][1]; + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + td[i][2] = this.data[i][1]/tot; + } + this.gridData = td; + }; + + $.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) { + var stack = []; + var td = []; + var tot = 0; + var sa = this.startAngle/180*Math.PI; + // don't know if we have any valid data yet, so set plot to not draw. + this._drawData = false; + for (var i=0; i<data.length; i++){ + if (this.data[i][1] != 0) { + // we have data, O.K. to draw. + this._drawData = true; + } + stack.push(data[i][1]); + td.push([data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + tot += data[i][1]; + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + td[i][2] = data[i][1]/tot; + } + return td; + }; + + $.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { + var r = this._diameter / 2; + var ri = r - this._thickness; + var fill = this.fill; + // var lineWidth = this.lineWidth; + ctx.save(); + ctx.translate(this._center[0], this._center[1]); + // ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2)); + + if (isShadow) { + for (var i=0; i<this.shadowDepth; i++) { + ctx.save(); + ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); + doDraw(); + } + } + + else { + doDraw(); + } + + function doDraw () { + // Fix for IE and Chrome that can't seem to draw circles correctly. + // ang2 should always be <= 2 pi since that is the way the data is converted. + if (ang2 > 6.282 + this.startAngle) { + ang2 = 6.282 + this.startAngle; + if (ang1 > ang2) { + ang1 = 6.281 + this.startAngle; + } + } + // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids + // ugly line on unfilled donuts. + if (ang1 >= ang2) { + return; + } + ctx.beginPath(); + ctx.fillStyle = color; + ctx.strokeStyle = color; + // ctx.lineWidth = lineWidth; + ctx.arc(0, 0, r, ang1, ang2, false); + ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2)); + ctx.arc(0,0, ri, ang2, ang1, true); + ctx.closePath(); + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + } + + if (isShadow) { + for (var i=0; i<this.shadowDepth; i++) { + ctx.restore(); + } + } + + ctx.restore(); + }; + + // called with scope of series + $.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) { + var i; + var opts = (options != undefined) ? options : {}; + // offset and direction of offset due to legend placement + var offx = 0; + var offy = 0; + var trans = 1; + // var colorGenerator = new this.colorGenerator(this.seriesColors); + if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { + var li = options.legendInfo; + switch (li.location) { + case 'nw': + offx = li.width + li.xoffset; + break; + case 'w': + offx = li.width + li.xoffset; + break; + case 'sw': + offx = li.width + li.xoffset; + break; + case 'ne': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'e': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'se': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'n': + offy = li.height + li.yoffset; + break; + case 's': + offy = li.height + li.yoffset; + trans = -1; + break; + default: + break; + } + } + + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var cw = ctx.canvas.width; + var ch = ctx.canvas.height; + var w = cw - offx - 2 * this.padding; + var h = ch - offy - 2 * this.padding; + var mindim = Math.min(w,h); + var d = mindim; + var ringmargin = (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin; + + for (var i=0; i<this._previousSeries.length; i++) { + d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin; + } + this._diameter = this.diameter || d; + if (this.innerDiameter != null) { + var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter; + this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0; + } + else { + this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85; + } + + var r = this._radius = this._diameter/2; + this._innerRadius = this._radius - this._thickness; + var sa = this.startAngle / 180 * Math.PI; + this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy]; + + if (this.shadow) { + var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; + for (var i=0; i<gd.length; i++) { + var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; + // Adjust ang1 and ang2 for sliceMargin + ang1 += this.sliceMargin/180*Math.PI; + this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true); + } + + } + for (var i=0; i<gd.length; i++) { + var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; + // Adjust ang1 and ang2 for sliceMargin + ang1 += this.sliceMargin/180*Math.PI; + var ang2 = gd[i][1] + sa; + this._sliceAngles.push([ang1, ang2]); + this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false); + + if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { + var fstr, avgang = (ang1+ang2)/2, label; + + if (this.dataLabels == 'label') { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, gd[i][0]); + } + else if (this.dataLabels == 'value') { + fstr = this.dataLabelFormatString || '%d'; + label = $.jqplot.sprintf(fstr, this.data[i][1]); + } + else if (this.dataLabels == 'percent') { + fstr = this.dataLabelFormatString || '%d%%'; + label = $.jqplot.sprintf(fstr, gd[i][2]*100); + } + else if (this.dataLabels.constructor == Array) { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, this.dataLabels[i]); + } + + var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; + + var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; + var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; + + var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); + x -= labelelem.width()/2; + y -= labelelem.height()/2; + x = Math.round(x); + y = Math.round(y); + labelelem.css({left: x, top: y}); + } + } + + }; + + $.jqplot.DonutAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer; + + + // There are no traditional axes on a donut chart. We just need to provide + // dummy objects with properties so the plot will render. + // called with scope of axis object. + $.jqplot.DonutAxisRenderer.prototype.init = function(options){ + // + this.tickRenderer = $.jqplot.DonutTickRenderer; + $.extend(true, this, options); + // I don't think I'm going to need _dataBounds here. + // have to go Axis scaling in a way to fit chart onto plot area + // and provide u2p and p2u functionality for mouse cursor, etc. + // for convienence set _dataBounds to 0 and 100 and + // set min/max to 0 and 100. + this._dataBounds = {min:0, max:100}; + this.min = 0; + this.max = 100; + this.showTicks = false; + this.ticks = []; + this.showMark = false; + this.show = false; + }; + + + + + $.jqplot.DonutLegendRenderer = function(){ + $.jqplot.TableLegendRenderer.call(this); + }; + + $.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer; + + /** + * Class: $.jqplot.DonutLegendRenderer + * Legend Renderer specific to donut plots. Set by default + * when user creates a donut plot. + */ + $.jqplot.DonutLegendRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: numberRows + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // prop: numberColumns + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + $.extend(true, this, options); + }; + + // called with context of legend + $.jqplot.DonutLegendRenderer.prototype.draw = function() { + var legend = this; + if (this.show) { + var series = this._series; + var ss = 'position:absolute;'; + ss += (this.background) ? 'background:'+this.background+';' : ''; + ss += (this.border) ? 'border:'+this.border+';' : ''; + ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; + ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; + ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; + ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; + ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; + ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; + this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); + // Donut charts legends don't go by number of series, but by number of data points + // in the series. Refactor things here for that. + + var pad = false, + reverse = false, + nr, nc; + var s = series[0]; + var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); + + if (s.show) { + var pd = s.data; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(pd.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(pd.length/this.numberColumns); + } + else { + nr = pd.length; + nc = 1; + } + + var i, j, tr, td1, td2, lt, rs, color; + var idx = 0; + + for (i=0; i<nr; i++) { + if (reverse){ + tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); + } + else{ + tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); + } + for (j=0; j<nc; j++) { + if (idx < pd.length){ + lt = this.labels[idx] || pd[idx][0].toString(); + color = colorGenerator.next(); + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ + '</div></td>'); + td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + td2.prependTo(tr); + td1.prependTo(tr); + } + else { + td1.appendTo(tr); + td2.appendTo(tr); + } + pad = true; + } + idx++; + } + } + } + } + return this._elem; + }; + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + // only set these if there is a donut series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.DonutRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer; + options.legend.renderer = $.jqplot.DonutLegendRenderer; + options.legend.preDraw = true; + options.seriesDefaults.pointLabels = {show: false}; + } + } + + // called with scope of plot. + function postInit(target, data, options) { + // if multiple series, add a reference to the previous one so that + // donut rings can nest. + for (var i=1; i<this.series.length; i++) { + if (!this.series[i]._previousSeries.length){ + for (var j=0; j<i; j++) { + if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) { + this.series[i]._previousSeries.push(this.series[j]); + } + } + } + } + for (i=0; i<this.series.length; i++) { + if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) { + this.series[i]._numberSeries = this.series.length; + // don't allow mouseover and mousedown at same time. + if (this.series[i].highlightMouseOver) { + this.series[i].highlightMouseDown = false; + } + } + } + } + + var postParseOptionsRun = false; + // called with scope of plot + function postParseOptions(options) { + for (var i=0; i<this.series.length; i++) { + this.series[i].seriesColors = this.seriesColors; + this.series[i].colorGenerator = $.jqplot.colorGenerator; + } + } + + function highlight (plot, sidx, pidx) { + var s = plot.series[sidx]; + var canvas = plot.plugins.donutRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.donutRenderer.highlightedSeriesIndex = sidx; + s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false); + } + + function unhighlight (plot) { + var canvas = plot.plugins.donutRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.donutRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + } + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) { + this.plugins.donutRenderer.highlightCanvas.resetCanvas(); + this.plugins.donutRenderer.highlightCanvas = null; + } + + this.plugins.donutRenderer = {highlightedSeriesIndex:null}; + this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + // do we have any data labels? if so, put highlight canvas before those + // Fix for broken jquery :first selector with canvas (VML) elements. + var labels = $(this.targetId+' .jqplot-data-label'); + if (labels.length) { + $(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); + } + // else put highlight canvas before event canvas. + else { + this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); + } + var hctx = this.plugins.donutRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + $.jqplot.preInitHooks.push(preInit); + + $.jqplot.DonutTickRenderer = function() { + $.jqplot.AxisTickRenderer.call(this); + }; + + $.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); + $.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer; + +})(jQuery); + + \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.donutRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.donutRenderer.min.js new file mode 100644 index 0000000..737e17c --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.donutRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(e){e.jqplot.DonutRenderer=function(){e.jqplot.LineRenderer.call(this)};e.jqplot.DonutRenderer.prototype=new e.jqplot.LineRenderer();e.jqplot.DonutRenderer.prototype.constructor=e.jqplot.DonutRenderer;e.jqplot.DonutRenderer.prototype.init=function(p,t){this.diameter=null;this.innerDiameter=null;this.thickness=null;this.padding=20;this.sliceMargin=0;this.ringMargin=null;this.fill=true;this.shadowOffset=2;this.shadowAlpha=0.07;this.shadowDepth=5;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.dataLabels="percent";this.showDataLabels=false;this.dataLabelFormatString=null;this.dataLabelThreshold=3;this.dataLabelPositionFactor=0.4;this.dataLabelNudge=0;this.startAngle=0;this.tickRenderer=e.jqplot.DonutTickRenderer;this._drawData=true;this._type="donut";if(p.highlightMouseDown&&p.highlightMouseOver==null){p.highlightMouseOver=false}e.extend(true,this,p);if(this.diameter!=null){this.diameter=this.diameter-this.sliceMargin}this._diameter=null;this._innerDiameter=null;this._radius=null;this._innerRadius=null;this._thickness=null;this._previousSeries=[];this._numberSeries=1;this._sliceAngles=[];this._highlightedPoint=null;if(this.highlightColors.length==0){for(var r=0;r<this.seriesColors.length;r++){var q=e.jqplot.getColorComponents(this.seriesColors[r]);var n=[q[0],q[1],q[2]];var s=n[0]+n[1]+n[2];for(var o=0;o<3;o++){n[o]=(s>570)?n[o]*0.8:n[o]+0.3*(255-n[o]);n[o]=parseInt(n[o],10)}this.highlightColors.push("rgb("+n[0]+","+n[1]+","+n[2]+")")}}t.postParseOptionsHooks.addOnce(l);t.postInitHooks.addOnce(g);t.eventListenerHooks.addOnce("jqplotMouseMove",b);t.eventListenerHooks.addOnce("jqplotMouseDown",a);t.eventListenerHooks.addOnce("jqplotMouseUp",j);t.eventListenerHooks.addOnce("jqplotClick",f);t.eventListenerHooks.addOnce("jqplotRightClick",m);t.postDrawHooks.addOnce(h)};e.jqplot.DonutRenderer.prototype.setGridData=function(s){var o=[];var t=[];var n=this.startAngle/180*Math.PI;var r=0;this._drawData=false;for(var q=0;q<this.data.length;q++){if(this.data[q][1]!=0){this._drawData=true}o.push(this.data[q][1]);t.push([this.data[q][0]]);if(q>0){o[q]+=o[q-1]}r+=this.data[q][1]}var p=Math.PI*2/o[o.length-1];for(var q=0;q<o.length;q++){t[q][1]=o[q]*p;t[q][2]=this.data[q][1]/r}this.gridData=t};e.jqplot.DonutRenderer.prototype.makeGridData=function(s,t){var o=[];var u=[];var r=0;var n=this.startAngle/180*Math.PI;this._drawData=false;for(var q=0;q<s.length;q++){if(this.data[q][1]!=0){this._drawData=true}o.push(s[q][1]);u.push([s[q][0]]);if(q>0){o[q]+=o[q-1]}r+=s[q][1]}var p=Math.PI*2/o[o.length-1];for(var q=0;q<o.length;q++){u[q][1]=o[q]*p;u[q][2]=s[q][1]/r}return u};e.jqplot.DonutRenderer.prototype.drawSlice=function(x,u,t,p,s){var n=this._diameter/2;var v=n-this._thickness;var w=this.fill;x.save();x.translate(this._center[0],this._center[1]);if(s){for(var q=0;q<this.shadowDepth;q++){x.save();x.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI),this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));o()}}else{o()}function o(){if(t>6.282+this.startAngle){t=6.282+this.startAngle;if(u>t){u=6.281+this.startAngle}}if(u>=t){return}x.beginPath();x.fillStyle=p;x.strokeStyle=p;x.arc(0,0,n,u,t,false);x.lineTo(v*Math.cos(t),v*Math.sin(t));x.arc(0,0,v,t,u,true);x.closePath();if(w){x.fill()}else{x.stroke()}}if(s){for(var q=0;q<this.shadowDepth;q++){x.restore()}}x.restore()};e.jqplot.DonutRenderer.prototype.draw=function(N,V,t,P){var Q;var J=(t!=undefined)?t:{};var q=0;var p=0;var u=1;if(t.legendInfo&&t.legendInfo.placement=="insideGrid"){var I=t.legendInfo;switch(I.location){case"nw":q=I.width+I.xoffset;break;case"w":q=I.width+I.xoffset;break;case"sw":q=I.width+I.xoffset;break;case"ne":q=I.width+I.xoffset;u=-1;break;case"e":q=I.width+I.xoffset;u=-1;break;case"se":q=I.width+I.xoffset;u=-1;break;case"n":p=I.height+I.yoffset;break;case"s":p=I.height+I.yoffset;u=-1;break;default:break}}var B=(J.shadow!=undefined)?J.shadow:this.shadow;var W=(J.showLine!=undefined)?J.showLine:this.showLine;var O=(J.fill!=undefined)?J.fill:this.fill;var s=N.canvas.width;var H=N.canvas.height;var G=s-q-2*this.padding;var R=H-p-2*this.padding;var v=Math.min(G,R);var T=v;var X=(this.ringMargin==null)?this.sliceMargin*2:this.ringMargin;for(var Q=0;Q<this._previousSeries.length;Q++){T-=2*this._previousSeries[Q]._thickness+2*X}this._diameter=this.diameter||T;if(this.innerDiameter!=null){var M=(this._numberSeries>1&&this.index>0)?this._previousSeries[0]._diameter:this._diameter;this._thickness=this.thickness||(M-this.innerDiameter-2*X*this._numberSeries)/this._numberSeries/2}else{this._thickness=this.thickness||v/2/(this._numberSeries+1)*0.85}var K=this._radius=this._diameter/2;this._innerRadius=this._radius-this._thickness;var o=this.startAngle/180*Math.PI;this._center=[(s-u*q)/2+u*q,(H-u*p)/2+u*p];if(this.shadow){var L="rgba(0,0,0,"+this.shadowAlpha+")";for(var Q=0;Q<V.length;Q++){var A=(Q==0)?o:V[Q-1][1]+o;A+=this.sliceMargin/180*Math.PI;this.renderer.drawSlice.call(this,N,A,V[Q][1]+o,L,true)}}for(var Q=0;Q<V.length;Q++){var A=(Q==0)?o:V[Q-1][1]+o;A+=this.sliceMargin/180*Math.PI;var z=V[Q][1]+o;this._sliceAngles.push([A,z]);this.renderer.drawSlice.call(this,N,A,z,this.seriesColors[Q],false);if(this.showDataLabels&&V[Q][2]*100>=this.dataLabelThreshold){var S,U=(A+z)/2,C;if(this.dataLabels=="label"){S=this.dataLabelFormatString||"%s";C=e.jqplot.sprintf(S,V[Q][0])}else{if(this.dataLabels=="value"){S=this.dataLabelFormatString||"%d";C=e.jqplot.sprintf(S,this.data[Q][1])}else{if(this.dataLabels=="percent"){S=this.dataLabelFormatString||"%d%%";C=e.jqplot.sprintf(S,V[Q][2]*100)}else{if(this.dataLabels.constructor==Array){S=this.dataLabelFormatString||"%s";C=e.jqplot.sprintf(S,this.dataLabels[Q])}}}}var n=this._innerRadius+this._thickness*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var F=this._center[0]+Math.cos(U)*n+this.canvas._offsets.left;var E=this._center[1]+Math.sin(U)*n+this.canvas._offsets.top;var D=e('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">'+C+"</span>").insertBefore(P.eventCanvas._elem);F-=D.width()/2;E-=D.height()/2;F=Math.round(F);E=Math.round(E);D.css({left:F,top:E})}}};e.jqplot.DonutAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.DonutAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.DonutAxisRenderer.prototype.constructor=e.jqplot.DonutAxisRenderer;e.jqplot.DonutAxisRenderer.prototype.init=function(n){this.tickRenderer=e.jqplot.DonutTickRenderer;e.extend(true,this,n);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};e.jqplot.DonutLegendRenderer=function(){e.jqplot.TableLegendRenderer.call(this)};e.jqplot.DonutLegendRenderer.prototype=new e.jqplot.TableLegendRenderer();e.jqplot.DonutLegendRenderer.prototype.constructor=e.jqplot.DonutLegendRenderer;e.jqplot.DonutLegendRenderer.prototype.init=function(n){this.numberRows=null;this.numberColumns=null;e.extend(true,this,n)};e.jqplot.DonutLegendRenderer.prototype.draw=function(){var q=this;if(this.show){var y=this._series;var B="position:absolute;";B+=(this.background)?"background:"+this.background+";":"";B+=(this.border)?"border:"+this.border+";":"";B+=(this.fontSize)?"font-size:"+this.fontSize+";":"";B+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";B+=(this.textColor)?"color:"+this.textColor+";":"";B+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";B+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";B+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";B+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=e('<table class="jqplot-table-legend" style="'+B+'"></table>');var F=false,x=false,n,v;var z=y[0];var o=new e.jqplot.ColorGenerator(z.seriesColors);if(z.show){var G=z.data;if(this.numberRows){n=this.numberRows;if(!this.numberColumns){v=Math.ceil(G.length/n)}else{v=this.numberColumns}}else{if(this.numberColumns){v=this.numberColumns;n=Math.ceil(G.length/this.numberColumns)}else{n=G.length;v=1}}var E,D,p,t,r,u,w,C;var A=0;for(E=0;E<n;E++){if(x){p=e('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem)}else{p=e('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem)}for(D=0;D<v;D++){if(A<G.length){u=this.labels[A]||G[A][0].toString();C=o.next();if(!x){if(E>0){F=true}else{F=false}}else{if(E==n-1){F=false}else{F=true}}w=(F)?this.rowSpacing:"0";t=e('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+w+';"><div><div class="jqplot-table-legend-swatch" style="border-color:'+C+';"></div></div></td>');r=e('<td class="jqplot-table-legend" style="padding-top:'+w+';"></td>');if(this.escapeHtml){r.text(u)}else{r.html(u)}if(x){r.prependTo(p);t.prependTo(p)}else{t.appendTo(p);r.appendTo(p)}F=true}A++}}}}return this._elem};function c(r,q,o){o=o||{};o.axesDefaults=o.axesDefaults||{};o.legend=o.legend||{};o.seriesDefaults=o.seriesDefaults||{};var n=false;if(o.seriesDefaults.renderer==e.jqplot.DonutRenderer){n=true}else{if(o.series){for(var p=0;p<o.series.length;p++){if(o.series[p].renderer==e.jqplot.DonutRenderer){n=true}}}}if(n){o.axesDefaults.renderer=e.jqplot.DonutAxisRenderer;o.legend.renderer=e.jqplot.DonutLegendRenderer;o.legend.preDraw=true;o.seriesDefaults.pointLabels={show:false}}}function g(r,q,o){for(var p=1;p<this.series.length;p++){if(!this.series[p]._previousSeries.length){for(var n=0;n<p;n++){if(this.series[p].renderer.constructor==e.jqplot.DonutRenderer&&this.series[n].renderer.constructor==e.jqplot.DonutRenderer){this.series[p]._previousSeries.push(this.series[n])}}}}for(p=0;p<this.series.length;p++){if(this.series[p].renderer.constructor==e.jqplot.DonutRenderer){this.series[p]._numberSeries=this.series.length;if(this.series[p].highlightMouseOver){this.series[p].highlightMouseDown=false}}}}var k=false;function l(n){for(var o=0;o<this.series.length;o++){this.series[o].seriesColors=this.seriesColors;this.series[o].colorGenerator=e.jqplot.colorGenerator}}function d(r,q,p){var o=r.series[q];var n=r.plugins.donutRenderer.highlightCanvas;n._ctx.clearRect(0,0,n._ctx.canvas.width,n._ctx.canvas.height);o._highlightedPoint=p;r.plugins.donutRenderer.highlightedSeriesIndex=q;o.renderer.drawSlice.call(o,n._ctx,o._sliceAngles[p][0],o._sliceAngles[p][1],o.highlightColors[p],false)}function i(p){var n=p.plugins.donutRenderer.highlightCanvas;n._ctx.clearRect(0,0,n._ctx.canvas.width,n._ctx.canvas.height);for(var o=0;o<p.series.length;o++){p.series[o]._highlightedPoint=null}p.plugins.donutRenderer.highlightedSeriesIndex=null;p.target.trigger("jqplotDataUnhighlight")}function b(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];var o=jQuery.Event("jqplotDataMouseOver");o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p);if(s.series[p[0]].highlightMouseOver&&!(p[0]==s.plugins.donutRenderer.highlightedSeriesIndex&&p[1]==s.series[p[0]]._highlightedPoint)){var n=jQuery.Event("jqplotDataHighlight");n.which=r.which;n.pageX=r.pageX;n.pageY=r.pageY;s.target.trigger(n,p);d(s,p[0],p[1])}}else{if(t==null){i(s)}}}function a(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];if(r.series[o[0]].highlightMouseDown&&!(o[0]==r.plugins.donutRenderer.highlightedSeriesIndex&&o[1]==r.series[o[0]]._highlightedPoint)){var n=jQuery.Event("jqplotDataHighlight");n.which=q.which;n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o);d(r,o[0],o[1])}}else{if(s==null){i(r)}}}function j(p,o,s,r,q){var n=q.plugins.donutRenderer.highlightedSeriesIndex;if(n!=null&&q.series[n].highlightMouseDown){i(q)}}function f(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];var n=jQuery.Event("jqplotDataClick");n.which=q.which;n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o)}}function m(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];var n=s.plugins.donutRenderer.highlightedSeriesIndex;if(n!=null&&s.series[n].highlightMouseDown){i(s)}var o=jQuery.Event("jqplotDataRightClick");o.which=r.which;o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p)}}function h(){if(this.plugins.donutRenderer&&this.plugins.donutRenderer.highlightCanvas){this.plugins.donutRenderer.highlightCanvas.resetCanvas();this.plugins.donutRenderer.highlightCanvas=null}this.plugins.donutRenderer={highlightedSeriesIndex:null};this.plugins.donutRenderer.highlightCanvas=new e.jqplot.GenericCanvas();var o=e(this.targetId+" .jqplot-data-label");if(o.length){e(o[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-donutRenderer-highlight-canvas",this._plotDimensions,this))}else{this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-donutRenderer-highlight-canvas",this._plotDimensions,this))}var n=this.plugins.donutRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(p){i(p.data.plot)})}e.jqplot.preInitHooks.push(c);e.jqplot.DonutTickRenderer=function(){e.jqplot.AxisTickRenderer.call(this)};e.jqplot.DonutTickRenderer.prototype=new e.jqplot.AxisTickRenderer();e.jqplot.DonutTickRenderer.prototype.constructor=e.jqplot.DonutTickRenderer})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.dragable.js b/web/extensions/jqplot/plugins/jqplot.dragable.js new file mode 100644 index 0000000..a9639bf --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.dragable.js @@ -0,0 +1,225 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + /** + * Class: $.jqplot.Dragable + * Plugin to make plotted points dragable by the user. + */ + $.jqplot.Dragable = function(options) { + // Group: Properties + this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); + this.shapeRenderer = new $.jqplot.ShapeRenderer(); + this.isDragging = false; + this.isOver = false; + this._ctx; + this._elem; + this._point; + this._gridData; + // prop: color + // CSS color spec for the dragged point (and adjacent line segment or bar). + this.color; + // prop: constrainTo + // Constrain dragging motion to an axis or to none. + // Allowable values are 'none', 'x', 'y' + this.constrainTo = 'none'; // 'x', 'y', or 'none'; + $.extend(true, this, options); + }; + + function DragCanvas() { + $.jqplot.GenericCanvas.call(this); + this.isDragging = false; + this.isOver = false; + this._neighbor; + this._cursors = []; + } + + DragCanvas.prototype = new $.jqplot.GenericCanvas(); + DragCanvas.prototype.constructor = DragCanvas; + + + // called within scope of series + $.jqplot.Dragable.parseOptions = function (defaults, opts) { + var options = opts || {}; + this.plugins.dragable = new $.jqplot.Dragable(options.dragable); + // since this function is called before series options are parsed, + // we can set this here and it will be overridden if needed. + this.isDragable = $.jqplot.config.enablePlugins; + }; + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas. + $.jqplot.Dragable.postPlotDraw = function() { + // Memory Leaks patch + if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) { + this.plugins.dragable.highlightCanvas.resetCanvas(); + this.plugins.dragable.highlightCanvas = null; + } + + this.plugins.dragable = {previousCursor:'auto', isOver:false}; + this.plugins.dragable.dragCanvas = new DragCanvas(); + + this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions, this)); + var dctx = this.plugins.dragable.dragCanvas.setContext(); + }; + + //$.jqplot.preInitHooks.push($.jqplot.Dragable.init); + $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Dragable.parseOptions); + $.jqplot.postDrawHooks.push($.jqplot.Dragable.postPlotDraw); + $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); + $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleDown]); + $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleUp]); + + + function initDragPoint(plot, neighbor) { + var s = plot.series[neighbor.seriesIndex]; + var drag = s.plugins.dragable; + + // first, init the mark renderer for the dragged point + var smr = s.markerRenderer; + var mr = drag.markerRenderer; + mr.style = smr.style; + mr.lineWidth = smr.lineWidth + 2.5; + mr.size = smr.size + 5; + if (!drag.color) { + var rgba = $.jqplot.getColorComponents(smr.color); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); + drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; + } + mr.color = drag.color; + mr.init(); + + var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0; + var end = neighbor.pointIndex+2; + drag._gridData = s.gridData.slice(start, end); + } + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (plot.plugins.dragable.dragCanvas.isDragging) { + var dc = plot.plugins.dragable.dragCanvas; + var dp = dc._neighbor; + var s = plot.series[dp.seriesIndex]; + var drag = s.plugins.dragable; + var gd = s.gridData; + + // compute the new grid position with any constraints. + var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x; + var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y; + + // compute data values for any listeners. + var xu = s._xaxis.series_p2u(x); + var yu = s._yaxis.series_p2u(y); + + // clear the canvas then redraw effect at new position. + var ctx = dc._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + // adjust our gridData for the new mouse position + if (dp.pointIndex > 0) { + drag._gridData[1] = [x, y]; + } + else { + drag._gridData[0] = [x, y]; + } + plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}}); + plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]); + } + else if (neighbor != null) { + var series = plot.series[neighbor.seriesIndex]; + if (series.isDragable) { + var dc = plot.plugins.dragable.dragCanvas; + if (!dc.isOver) { + dc._cursors.push(ev.target.style.cursor); + ev.target.style.cursor = "pointer"; + } + dc.isOver = true; + } + } + else if (neighbor == null) { + var dc = plot.plugins.dragable.dragCanvas; + if (dc.isOver) { + ev.target.style.cursor = dc._cursors.pop(); + dc.isOver = false; + } + } + } + + function handleDown(ev, gridpos, datapos, neighbor, plot) { + var dc = plot.plugins.dragable.dragCanvas; + dc._cursors.push(ev.target.style.cursor); + if (neighbor != null) { + var s = plot.series[neighbor.seriesIndex]; + var drag = s.plugins.dragable; + if (s.isDragable && !dc.isDragging) { + dc._neighbor = neighbor; + dc.isDragging = true; + initDragPoint(plot, neighbor); + drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx); + ev.target.style.cursor = "move"; + plot.target.trigger('jqplotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]); + } + } + // Just in case of a hickup, we'll clear the drag canvas and reset. + else { + var ctx = dc._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + dc.isDragging = false; + } + } + + function handleUp(ev, gridpos, datapos, neighbor, plot) { + if (plot.plugins.dragable.dragCanvas.isDragging) { + var dc = plot.plugins.dragable.dragCanvas; + // clear the canvas + var ctx = dc._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + dc.isDragging = false; + // redraw the series canvas at the new point. + var dp = dc._neighbor; + var s = plot.series[dp.seriesIndex]; + var drag = s.plugins.dragable; + // compute the new grid position with any constraints. + var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis]; + var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis]; + // var x = datapos[s.xaxis]; + // var y = datapos[s.yaxis]; + s.data[dp.pointIndex][0] = x; + s.data[dp.pointIndex][1] = y; + plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex); + dc._neighbor = null; + ev.target.style.cursor = dc._cursors.pop(); + plot.target.trigger('jqplotDragStop', [gridpos, datapos]); + } + } +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.dragable.min.js b/web/extensions/jqplot/plugins/jqplot.dragable.min.js new file mode 100644 index 0000000..83c8e65 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.dragable.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(d){d.jqplot.Dragable=function(g){this.markerRenderer=new d.jqplot.MarkerRenderer({shadow:false});this.shapeRenderer=new d.jqplot.ShapeRenderer();this.isDragging=false;this.isOver=false;this._ctx;this._elem;this._point;this._gridData;this.color;this.constrainTo="none";d.extend(true,this,g)};function b(){d.jqplot.GenericCanvas.call(this);this.isDragging=false;this.isOver=false;this._neighbor;this._cursors=[]}b.prototype=new d.jqplot.GenericCanvas();b.prototype.constructor=b;d.jqplot.Dragable.parseOptions=function(i,h){var g=h||{};this.plugins.dragable=new d.jqplot.Dragable(g.dragable);this.isDragable=d.jqplot.config.enablePlugins};d.jqplot.Dragable.postPlotDraw=function(){if(this.plugins.dragable&&this.plugins.dragable.highlightCanvas){this.plugins.dragable.highlightCanvas.resetCanvas();this.plugins.dragable.highlightCanvas=null}this.plugins.dragable={previousCursor:"auto",isOver:false};this.plugins.dragable.dragCanvas=new b();this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding,"jqplot-dragable-canvas",this._plotDimensions,this));var g=this.plugins.dragable.dragCanvas.setContext()};d.jqplot.preParseSeriesOptionsHooks.push(d.jqplot.Dragable.parseOptions);d.jqplot.postDrawHooks.push(d.jqplot.Dragable.postPlotDraw);d.jqplot.eventListenerHooks.push(["jqplotMouseMove",e]);d.jqplot.eventListenerHooks.push(["jqplotMouseDown",c]);d.jqplot.eventListenerHooks.push(["jqplotMouseUp",a]);function f(n,p){var q=n.series[p.seriesIndex];var m=q.plugins.dragable;var h=q.markerRenderer;var i=m.markerRenderer;i.style=h.style;i.lineWidth=h.lineWidth+2.5;i.size=h.size+5;if(!m.color){var l=d.jqplot.getColorComponents(h.color);var o=[l[0],l[1],l[2]];var k=(l[3]>=0.6)?l[3]*0.6:l[3]*(2-l[3]);m.color="rgba("+o[0]+","+o[1]+","+o[2]+","+k+")"}i.color=m.color;i.init();var g=(p.pointIndex>0)?p.pointIndex-1:0;var j=p.pointIndex+2;m._gridData=q.gridData.slice(g,j)}function e(o,l,h,t,m){if(m.plugins.dragable.dragCanvas.isDragging){var u=m.plugins.dragable.dragCanvas;var i=u._neighbor;var w=m.series[i.seriesIndex];var k=w.plugins.dragable;var r=w.gridData;var p=(k.constrainTo=="y")?i.gridData[0]:l.x;var n=(k.constrainTo=="x")?i.gridData[1]:l.y;var g=w._xaxis.series_p2u(p);var q=w._yaxis.series_p2u(n);var v=u._ctx;v.clearRect(0,0,v.canvas.width,v.canvas.height);if(i.pointIndex>0){k._gridData[1]=[p,n]}else{k._gridData[0]=[p,n]}m.series[i.seriesIndex].draw(u._ctx,{gridData:k._gridData,shadow:false,preventJqPlotSeriesDrawTrigger:true,color:k.color,markerOptions:{color:k.color,shadow:false},trendline:{show:false}});m.target.trigger("jqplotSeriesPointChange",[i.seriesIndex,i.pointIndex,[g,q],[p,n]])}else{if(t!=null){var j=m.series[t.seriesIndex];if(j.isDragable){var u=m.plugins.dragable.dragCanvas;if(!u.isOver){u._cursors.push(o.target.style.cursor);o.target.style.cursor="pointer"}u.isOver=true}}else{if(t==null){var u=m.plugins.dragable.dragCanvas;if(u.isOver){o.target.style.cursor=u._cursors.pop();u.isOver=false}}}}}function c(k,i,g,l,j){var m=j.plugins.dragable.dragCanvas;m._cursors.push(k.target.style.cursor);if(l!=null){var o=j.series[l.seriesIndex];var h=o.plugins.dragable;if(o.isDragable&&!m.isDragging){m._neighbor=l;m.isDragging=true;f(j,l);h.markerRenderer.draw(o.gridData[l.pointIndex][0],o.gridData[l.pointIndex][1],m._ctx);k.target.style.cursor="move";j.target.trigger("jqplotDragStart",[l.seriesIndex,l.pointIndex,i,g])}}else{var n=m._ctx;n.clearRect(0,0,n.canvas.width,n.canvas.height);m.isDragging=false}}function a(m,j,g,o,k){if(k.plugins.dragable.dragCanvas.isDragging){var p=k.plugins.dragable.dragCanvas;var q=p._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);p.isDragging=false;var h=p._neighbor;var r=k.series[h.seriesIndex];var i=r.plugins.dragable;var n=(i.constrainTo=="y")?h.data[0]:g[r.xaxis];var l=(i.constrainTo=="x")?h.data[1]:g[r.yaxis];r.data[h.pointIndex][0]=n;r.data[h.pointIndex][1]=l;k.drawSeries({preventJqPlotSeriesDrawTrigger:true},h.seriesIndex);p._neighbor=null;m.target.style.cursor=p._cursors.pop();k.target.trigger("jqplotDragStop",[j,g])}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.js b/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.js new file mode 100644 index 0000000..5a2cbc6 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.js @@ -0,0 +1,305 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + // class $.jqplot.EnhancedLegendRenderer + // Legend renderer which can specify the number of rows and/or columns in the legend. + $.jqplot.EnhancedLegendRenderer = function(){ + $.jqplot.TableLegendRenderer.call(this); + }; + + $.jqplot.EnhancedLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.EnhancedLegendRenderer.prototype.constructor = $.jqplot.EnhancedLegendRenderer; + + // called with scope of legend. + $.jqplot.EnhancedLegendRenderer.prototype.init = function(options) { + // prop: numberRows + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // prop: numberColumns + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + // prop: seriesToggle + // false to not enable series on/off toggling on the legend. + // true or a fadein/fadeout speed (number of milliseconds or 'fast', 'normal', 'slow') + // to enable show/hide of series on click of legend item. + this.seriesToggle = 'normal'; + // prop: seriesToggleReplot + // True to replot the chart after toggling series on/off. + // This will set the series show property to false. + // This allows for rescaling or other maniplation of chart. + // Set to an options object (e.g. {resetAxes: true}) for replot options. + this.seriesToggleReplot = false; + // prop: disableIEFading + // true to toggle series with a show/hide method only and not allow fading in/out. + // This is to overcome poor performance of fade in some versions of IE. + this.disableIEFading = true; + $.extend(true, this, options); + + if (this.seriesToggle) { + $.jqplot.postDrawHooks.push(postDraw); + } + }; + + // called with scope of legend + $.jqplot.EnhancedLegendRenderer.prototype.draw = function(offsets, plot) { + var legend = this; + if (this.show) { + var series = this._series; + var s; + var ss = 'position:absolute;'; + ss += (this.background) ? 'background:'+this.background+';' : ''; + ss += (this.border) ? 'border:'+this.border+';' : ''; + ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; + ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; + ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; + ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; + ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; + ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; + this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); + if (this.seriesToggle) { + this._elem.css('z-index', '3'); + } + + var pad = false, + reverse = false, + nr, nc; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(series.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(series.length/this.numberColumns); + } + else { + nr = series.length; + nc = 1; + } + + var i, j, tr, td1, td2, lt, rs, div, div0, div1; + var idx = 0; + // check to see if we need to reverse + for (i=series.length-1; i>=0; i--) { + if (nc == 1 && series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){ + reverse = true; + } + } + + for (i=0; i<nr; i++) { + tr = $(document.createElement('tr')); + tr.addClass('jqplot-table-legend'); + if (reverse){ + tr.prependTo(this._elem); + } + else{ + tr.appendTo(this._elem); + } + for (j=0; j<nc; j++) { + if (idx < series.length && (series[idx].show || series[idx].showLabel)){ + s = series[idx]; + lt = this.labels[idx] || s.label.toString(); + if (lt) { + var color = s.color; + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + td1 = $(document.createElement('td')); + td1.addClass('jqplot-table-legend jqplot-table-legend-swatch'); + td1.css({textAlign: 'center', paddingTop: rs}); + + div0 = $(document.createElement('div')); + div0.addClass('jqplot-table-legend-swatch-outline'); + div1 = $(document.createElement('div')); + div1.addClass('jqplot-table-legend-swatch'); + div1.css({backgroundColor: color, borderColor: color}); + + td1.append(div0.append(div1)); + + td2 = $(document.createElement('td')); + td2.addClass('jqplot-table-legend jqplot-table-legend-label'); + td2.css('paddingTop', rs); + + // td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ + // '</div></td>'); + // td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + if (this.showLabels) {td2.prependTo(tr);} + if (this.showSwatches) {td1.prependTo(tr);} + } + else { + if (this.showSwatches) {td1.appendTo(tr);} + if (this.showLabels) {td2.appendTo(tr);} + } + + if (this.seriesToggle) { + + // add an overlay for clicking series on/off + // div0 = $(document.createElement('div')); + // div0.addClass('jqplot-table-legend-overlay'); + // div0.css({position:'relative', left:0, top:0, height:'100%', width:'100%'}); + // tr.append(div0); + + var speed; + if (typeof(this.seriesToggle) === 'string' || typeof(this.seriesToggle) === 'number') { + if (!$.jqplot.use_excanvas || !this.disableIEFading) { + speed = this.seriesToggle; + } + } + if (this.showSwatches) { + td1.bind('click', {series:s, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); + td1.addClass('jqplot-seriesToggle'); + } + if (this.showLabels) { + td2.bind('click', {series:s, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); + td2.addClass('jqplot-seriesToggle'); + } + + // for series that are already hidden, add the hidden class + if (!s.show && s.showLabel) { + td1.addClass('jqplot-series-hidden'); + td2.addClass('jqplot-series-hidden'); + } + } + + pad = true; + } + } + idx++; + } + + td1 = td2 = div0 = div1 = null; + } + } + return this._elem; + }; + + var handleToggle = function (ev) { + var d = ev.data, + s = d.series, + replot = d.replot, + plot = d.plot, + speed = d.speed, + sidx = s.index, + showing = false; + + if (s.canvas._elem.is(':hidden') || !s.show) { + showing = true; + } + + var doLegendToggle = function() { + + if (replot) { + var opts = {}; + + if ($.isPlainObject(replot)) { + $.extend(true, opts, replot); + } + + plot.replot(opts); + // if showing, there was no canvas element to fade in, so hide here + // and then do a fade in. + if (showing && speed) { + var s = plot.series[sidx]; + + if (s.shadowCanvas._elem) { + s.shadowCanvas._elem.hide().fadeIn(speed); + } + s.canvas._elem.hide().fadeIn(speed); + s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide().fadeIn(speed); + } + + } + + else { + var s = plot.series[sidx]; + + if (s.canvas._elem.is(':hidden') || !s.show) { + // Not sure if there is a better way to check for showSwatches and showLabels === true. + // Test for "undefined" since default values for both showSwatches and showLables is true. + if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) { + plot.legend._elem.find('td').eq(sidx * 2).addClass('jqplot-series-hidden'); + } + if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) { + plot.legend._elem.find('td').eq((sidx * 2) + 1).addClass('jqplot-series-hidden'); + } + } + else { + if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) { + plot.legend._elem.find('td').eq(sidx * 2).removeClass('jqplot-series-hidden'); + } + if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) { + plot.legend._elem.find('td').eq((sidx * 2) + 1).removeClass('jqplot-series-hidden'); + } + } + + } + + }; + + s.toggleDisplay(ev, doLegendToggle); + }; + + // called with scope of plot. + var postDraw = function () { + if (this.legend.renderer.constructor == $.jqplot.EnhancedLegendRenderer && this.legend.seriesToggle){ + var e = this.legend._elem.detach(); + this.eventCanvas._elem.after(e); + } + }; +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js new file mode 100644 index 0000000..968e77c --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(c){c.jqplot.EnhancedLegendRenderer=function(){c.jqplot.TableLegendRenderer.call(this)};c.jqplot.EnhancedLegendRenderer.prototype=new c.jqplot.TableLegendRenderer();c.jqplot.EnhancedLegendRenderer.prototype.constructor=c.jqplot.EnhancedLegendRenderer;c.jqplot.EnhancedLegendRenderer.prototype.init=function(d){this.numberRows=null;this.numberColumns=null;this.seriesToggle="normal";this.seriesToggleReplot=false;this.disableIEFading=true;c.extend(true,this,d);if(this.seriesToggle){c.jqplot.postDrawHooks.push(b)}};c.jqplot.EnhancedLegendRenderer.prototype.draw=function(m,y){var f=this;if(this.show){var r=this._series;var u;var w="position:absolute;";w+=(this.background)?"background:"+this.background+";":"";w+=(this.border)?"border:"+this.border+";":"";w+=(this.fontSize)?"font-size:"+this.fontSize+";":"";w+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";w+=(this.textColor)?"color:"+this.textColor+";":"";w+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";w+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";w+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";w+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=c('<table class="jqplot-table-legend" style="'+w+'"></table>');if(this.seriesToggle){this._elem.css("z-index","3")}var C=false,q=false,d,o;if(this.numberRows){d=this.numberRows;if(!this.numberColumns){o=Math.ceil(r.length/d)}else{o=this.numberColumns}}else{if(this.numberColumns){o=this.numberColumns;d=Math.ceil(r.length/this.numberColumns)}else{d=r.length;o=1}}var B,z,e,l,k,n,p,t,h,g;var v=0;for(B=r.length-1;B>=0;B--){if(o==1&&r[B]._stack||r[B].renderer.constructor==c.jqplot.BezierCurveRenderer){q=true}}for(B=0;B<d;B++){e=c(document.createElement("tr"));e.addClass("jqplot-table-legend");if(q){e.prependTo(this._elem)}else{e.appendTo(this._elem)}for(z=0;z<o;z++){if(v<r.length&&(r[v].show||r[v].showLabel)){u=r[v];n=this.labels[v]||u.label.toString();if(n){var x=u.color;if(!q){if(B>0){C=true}else{C=false}}else{if(B==d-1){C=false}else{C=true}}p=(C)?this.rowSpacing:"0";l=c(document.createElement("td"));l.addClass("jqplot-table-legend jqplot-table-legend-swatch");l.css({textAlign:"center",paddingTop:p});h=c(document.createElement("div"));h.addClass("jqplot-table-legend-swatch-outline");g=c(document.createElement("div"));g.addClass("jqplot-table-legend-swatch");g.css({backgroundColor:x,borderColor:x});l.append(h.append(g));k=c(document.createElement("td"));k.addClass("jqplot-table-legend jqplot-table-legend-label");k.css("paddingTop",p);if(this.escapeHtml){k.text(n)}else{k.html(n)}if(q){if(this.showLabels){k.prependTo(e)}if(this.showSwatches){l.prependTo(e)}}else{if(this.showSwatches){l.appendTo(e)}if(this.showLabels){k.appendTo(e)}}if(this.seriesToggle){var A;if(typeof(this.seriesToggle)==="string"||typeof(this.seriesToggle)==="number"){if(!c.jqplot.use_excanvas||!this.disableIEFading){A=this.seriesToggle}}if(this.showSwatches){l.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);l.addClass("jqplot-seriesToggle")}if(this.showLabels){k.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);k.addClass("jqplot-seriesToggle")}if(!u.show&&u.showLabel){l.addClass("jqplot-series-hidden");k.addClass("jqplot-series-hidden")}}C=true}}v++}l=k=h=g=null}}return this._elem};var a=function(j){var i=j.data,m=i.series,k=i.replot,h=i.plot,f=i.speed,l=m.index,g=false;if(m.canvas._elem.is(":hidden")||!m.show){g=true}var e=function(){if(k){var n={};if(c.isPlainObject(k)){c.extend(true,n,k)}h.replot(n);if(g&&f){var d=h.series[l];if(d.shadowCanvas._elem){d.shadowCanvas._elem.hide().fadeIn(f)}d.canvas._elem.hide().fadeIn(f);d.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+d.index).hide().fadeIn(f)}}else{var d=h.series[l];if(d.canvas._elem.is(":hidden")||!d.show){if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).addClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).addClass("jqplot-series-hidden")}}else{if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).removeClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).removeClass("jqplot-series-hidden")}}}};m.toggleDisplay(j,e)};var b=function(){if(this.legend.renderer.constructor==c.jqplot.EnhancedLegendRenderer&&this.legend.seriesToggle){var d=this.legend._elem.detach();this.eventCanvas._elem.after(d)}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.funnelRenderer.js b/web/extensions/jqplot/plugins/jqplot.funnelRenderer.js new file mode 100644 index 0000000..2cbc9c2 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.funnelRenderer.js @@ -0,0 +1,943 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.FunnelRenderer + * Plugin renderer to draw a funnel chart. + * x values, if present, will be used as labels. + * y values give area size. + * + * Funnel charts will draw a single series + * only. + * + * To use this renderer, you need to include the + * funnel renderer plugin, for example: + * + * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script> + * + * Properties described here are passed into the $.jqplot function + * as options on the series renderer. For example: + * + * > plot2 = $.jqplot('chart2', [s1, s2], { + * > seriesDefaults: { + * > renderer:$.jqplot.FunnelRenderer, + * > rendererOptions:{ + * > sectionMargin: 12, + * > widthRatio: 0.3 + * > } + * > } + * > }); + * + * IMPORTANT + * + * *The funnel renderer will reorder data in descending order* so the largest value in + * the data set is first and displayed on top of the funnel. Data will then + * be displayed in descending order down the funnel. The area of each funnel + * section will correspond to the value of each data point relative to the sum + * of all values. That is section area is proportional to section value divided by + * sum of all section values. + * + * If your data is not in descending order when passed into the plot, *it will be + * reordered* when stored in the series.data property. A copy of the unordered + * data is kept in the series._unorderedData property. + * + * A funnel plot will trigger events on the plot target + * according to user interaction. All events return the event object, + * the series index, the point (section) index, and the point data for + * the appropriate section. *Note* the point index will referr to the ordered + * data, not the original unordered data. + * + * 'jqplotDataMouseOver' - triggered when mousing over a section. + * 'jqplotDataHighlight' - triggered the first time user mouses over a section, + * if highlighting is enabled. + * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of + * a highlighted section. + * 'jqplotDataClick' - triggered when the user clicks on a section. + * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if + * the "captureRightClick" option is set to true on the plot. + */ + $.jqplot.FunnelRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer; + + // called with scope of a series + $.jqplot.FunnelRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + // prop: padding + // padding between the funnel and plot edges, legend, etc. + this.padding = {top: 20, right: 20, bottom: 20, left: 20}; + // prop: sectionMargin + // spacing between funnel sections in pixels. + this.sectionMargin = 6; + // prop: fill + // true or false, whether to fill the areas. + this.fill = true; + // prop: shadowOffset + // offset of the shadow from the area and offset of + // each succesive stroke of the shadow from the last. + this.shadowOffset = 2; + // prop: shadowAlpha + // transparency of the shadow (0 = transparent, 1 = opaque) + this.shadowAlpha = 0.07; + // prop: shadowDepth + // number of strokes to apply to the shadow, + // each stroke offset shadowOffset from the last. + this.shadowDepth = 5; + // prop: highlightMouseOver + // True to highlight area when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a area. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a area. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // array of colors to use when highlighting an area. + this.highlightColors = []; + // prop: widthRatio + // The ratio of the width of the top of the funnel to the bottom. + // a ratio of 0 will make an upside down pyramid. + this.widthRatio = 0.2; + // prop: lineWidth + // width of line if areas are stroked and not filled. + this.lineWidth = 2; + // prop: dataLabels + // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. + // Defaults to percentage of each pie slice. + this.dataLabels = 'percent'; + // prop: showDataLabels + // true to show data labels on slices. + this.showDataLabels = false; + // prop: dataLabelFormatString + // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. + this.dataLabelFormatString = null; + // prop: dataLabelThreshold + // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. + // This applies to all label types, not just to percentage labels. + this.dataLabelThreshold = 3; + this._type = 'funnel'; + + this.tickRenderer = $.jqplot.FunnelTickRenderer; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + $.extend(true, this, options); + + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + + // lengths of bases, or horizontal sides of areas of trapezoid. + this._bases = []; + // total area + this._atot; + // areas of segments. + this._areas = []; + // vertical lengths of segments. + this._lengths = []; + // angle of the funnel to vertical. + this._angle; + this._dataIndices = []; + + // sort data + this._unorderedData = $.extend(true, [], this.data); + var idxs = $.extend(true, [], this.data); + for (var i=0; i<idxs.length; i++) { + idxs[i].push(i); + } + this.data.sort( function (a, b) { return b[1] - a[1]; } ); + idxs.sort( function (a, b) { return b[1] - a[1]; }); + for (var i=0; i<idxs.length; i++) { + this._dataIndices.push(idxs[i][2]); + } + + // set highlight colors if none provided + if (this.highlightColors.length == 0) { + for (var i=0; i<this.seriesColors.length; i++){ + var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); + } + } + + plot.postParseOptionsHooks.addOnce(postParseOptions); + plot.postInitHooks.addOnce(postInit); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + plot.postDrawHooks.addOnce(postPlotDraw); + + }; + + // gridData will be of form [label, percentage of total] + $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) { + // set gridData property. This will hold angle in radians of each data point. + var sum = 0; + var td = []; + for (var i=0; i<this.data.length; i++){ + sum += this.data[i][1]; + td.push([this.data[i][0], this.data[i][1]]); + } + + // normalize y values, so areas are proportional. + for (var i=0; i<td.length; i++) { + td[i][1] = td[i][1]/sum; + } + + this._bases = new Array(td.length + 1); + this._lengths = new Array(td.length); + + this.gridData = td; + }; + + $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) { + // set gridData property. This will hold angle in radians of each data point. + var sum = 0; + var td = []; + for (var i=0; i<this.data.length; i++){ + sum += this.data[i][1]; + td.push([this.data[i][0], this.data[i][1]]); + } + + // normalize y values, so areas are proportional. + for (var i=0; i<td.length; i++) { + td[i][1] = td[i][1]/sum; + } + + this._bases = new Array(td.length + 1); + this._lengths = new Array(td.length); + + return td; + }; + + $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) { + var fill = this.fill; + var lineWidth = this.lineWidth; + ctx.save(); + + if (isShadow) { + for (var i=0; i<this.shadowDepth; i++) { + ctx.save(); + ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); + doDraw(); + } + } + + else { + doDraw(); + } + + function doDraw () { + ctx.beginPath(); + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + ctx.moveTo(vertices[0][0], vertices[0][1]); + for (var i=1; i<4; i++) { + ctx.lineTo(vertices[i][0], vertices[i][1]); + } + ctx.closePath(); + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + } + + if (isShadow) { + for (var i=0; i<this.shadowDepth; i++) { + ctx.restore(); + } + } + + ctx.restore(); + }; + + // called with scope of series + $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) { + var i; + var opts = (options != undefined) ? options : {}; + // offset and direction of offset due to legend placement + var offx = 0; + var offy = 0; + var trans = 1; + this._areas = []; + // var colorGenerator = new this.colorGenerator(this.seriesColors); + if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { + var li = options.legendInfo; + switch (li.location) { + case 'nw': + offx = li.width + li.xoffset; + break; + case 'w': + offx = li.width + li.xoffset; + break; + case 'sw': + offx = li.width + li.xoffset; + break; + case 'ne': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'e': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'se': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'n': + offy = li.height + li.yoffset; + break; + case 's': + offy = li.height + li.yoffset; + trans = -1; + break; + default: + break; + } + } + + var loff = (trans==1) ? this.padding.left + offx : this.padding.left; + var toff = (trans==1) ? this.padding.top + offy : this.padding.top; + var roff = (trans==-1) ? this.padding.right + offx : this.padding.right; + var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom; + + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var cw = ctx.canvas.width; + var ch = ctx.canvas.height; + this._bases[0] = cw - loff - roff; + var ltot = this._length = ch - toff - boff; + + var hend = this._bases[0]*this.widthRatio; + this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio); + + this._angle = Math.atan((this._bases[0] - hend)/2/ltot); + + for (i=0; i<gd.length; i++) { + this._areas.push(gd[i][1] * this._atot); + } + + + var guess, err, count, lsum=0; + var tolerance = 0.0001; + + for (i=0; i<this._areas.length; i++) { + guess = this._areas[i]/this._bases[i]; + err = 999999; + this._lengths[i] = guess; + count = 0; + while (err > this._lengths[i]*tolerance && count < 100) { + this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle)); + err = Math.abs(this._lengths[i] - guess); + this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle)); + guess = this._lengths[i]; + count++; + } + lsum += this._lengths[i]; + } + + // figure out vertices of each section + this._vertices = new Array(gd.length); + + // these are 4 coners of entire trapezoid + var p0 = [loff, toff], + p1 = [loff+this._bases[0], toff], + p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length], + p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]]; + + // equations of right and left sides, returns x, y values given height of section (y value) + function findleft (l) { + var m = (p0[1] - p2[1])/(p0[0] - p2[0]); + var b = p0[1] - m*p0[0]; + var y = l + p0[1]; + + return [(y - b)/m, y]; + } + + function findright (l) { + var m = (p1[1] - p3[1])/(p1[0] - p3[0]); + var b = p1[1] - m*p1[0]; + var y = l + p1[1]; + + return [(y - b)/m, y]; + } + + var x = offx, y = offy; + var h=0, adj=0; + + for (i=0; i<gd.length; i++) { + this._vertices[i] = new Array(); + var v = this._vertices[i]; + var sm = this.sectionMargin; + if (i == 0) { + adj = 0; + } + if (i == 1) { + adj = sm/3; + } + else if (i > 0 && i < gd.length-1) { + adj = sm/2; + } + else if (i == gd.length -1) { + adj = 2*sm/3; + } + v.push(findleft(h+adj)); + v.push(findright(h+adj)); + h += this._lengths[i]; + if (i == 0) { + adj = -2*sm/3; + } + else if (i > 0 && i < gd.length-1) { + adj = -sm/2; + } + else if (i == gd.length - 1) { + adj = 0; + } + v.push(findright(h+adj)); + v.push(findleft(h+adj)); + + } + + if (this.shadow) { + var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; + for (var i=0; i<gd.length; i++) { + this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true); + } + + } + for (var i=0; i<gd.length; i++) { + var v = this._vertices[i]; + this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]); + + if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) { + var fstr, label; + + if (this.dataLabels == 'label') { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, gd[i][0]); + } + else if (this.dataLabels == 'value') { + fstr = this.dataLabelFormatString || '%d'; + label = $.jqplot.sprintf(fstr, this.data[i][1]); + } + else if (this.dataLabels == 'percent') { + fstr = this.dataLabelFormatString || '%d%%'; + label = $.jqplot.sprintf(fstr, gd[i][1]*100); + } + else if (this.dataLabels.constructor == Array) { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]); + } + + var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; + + var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left; + var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top; + + var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); + x -= labelelem.width()/2; + y -= labelelem.height()/2; + x = Math.round(x); + y = Math.round(y); + labelelem.css({left: x, top: y}); + } + + } + + }; + + $.jqplot.FunnelAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer; + + + // There are no traditional axes on a funnel chart. We just need to provide + // dummy objects with properties so the plot will render. + // called with scope of axis object. + $.jqplot.FunnelAxisRenderer.prototype.init = function(options){ + // + this.tickRenderer = $.jqplot.FunnelTickRenderer; + $.extend(true, this, options); + // I don't think I'm going to need _dataBounds here. + // have to go Axis scaling in a way to fit chart onto plot area + // and provide u2p and p2u functionality for mouse cursor, etc. + // for convienence set _dataBounds to 0 and 100 and + // set min/max to 0 and 100. + this._dataBounds = {min:0, max:100}; + this.min = 0; + this.max = 100; + this.showTicks = false; + this.ticks = []; + this.showMark = false; + this.show = false; + }; + + + + /** + * Class: $.jqplot.FunnelLegendRenderer + * Legend Renderer specific to funnel plots. Set by default + * when the user creates a funnel plot. + */ + $.jqplot.FunnelLegendRenderer = function(){ + $.jqplot.TableLegendRenderer.call(this); + }; + + $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer; + + $.jqplot.FunnelLegendRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: numberRows + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // prop: numberColumns + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + $.extend(true, this, options); + }; + + // called with context of legend + $.jqplot.FunnelLegendRenderer.prototype.draw = function() { + var legend = this; + if (this.show) { + var series = this._series; + var ss = 'position:absolute;'; + ss += (this.background) ? 'background:'+this.background+';' : ''; + ss += (this.border) ? 'border:'+this.border+';' : ''; + ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; + ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; + ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; + ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; + ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; + ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; + this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); + // Funnel charts legends don't go by number of series, but by number of data points + // in the series. Refactor things here for that. + + var pad = false, + reverse = false, + nr, nc; + var s = series[0]; + var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); + + if (s.show) { + var pd = s.data; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(pd.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(pd.length/this.numberColumns); + } + else { + nr = pd.length; + nc = 1; + } + + var i, j, tr, td1, td2, lt, rs, color; + var idx = 0; + + for (i=0; i<nr; i++) { + if (reverse){ + tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); + } + else{ + tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); + } + for (j=0; j<nc; j++) { + if (idx < pd.length){ + lt = this.labels[idx] || pd[idx][0].toString(); + color = colorGenerator.next(); + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ + '</div></td>'); + td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + td2.prependTo(tr); + td1.prependTo(tr); + } + else { + td1.appendTo(tr); + td2.appendTo(tr); + } + pad = true; + } + idx++; + } + } + } + } + return this._elem; + }; + + // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) { + // if (this.show) { + // // fake a grid for positioning + // var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; + // if (this.placement == 'insideGrid') { + // switch (this.location) { + // case 'nw': + // var a = grid._left + this.xoffset; + // var b = grid._top + this.yoffset; + // this._elem.css('left', a); + // this._elem.css('top', b); + // break; + // case 'n': + // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + // var b = grid._top + this.yoffset; + // this._elem.css('left', a); + // this._elem.css('top', b); + // break; + // case 'ne': + // var a = offsets.right + this.xoffset; + // var b = grid._top + this.yoffset; + // this._elem.css({right:a, top:b}); + // break; + // case 'e': + // var a = offsets.right + this.xoffset; + // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + // this._elem.css({right:a, top:b}); + // break; + // case 'se': + // var a = offsets.right + this.xoffset; + // var b = offsets.bottom + this.yoffset; + // this._elem.css({right:a, bottom:b}); + // break; + // case 's': + // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + // var b = offsets.bottom + this.yoffset; + // this._elem.css({left:a, bottom:b}); + // break; + // case 'sw': + // var a = grid._left + this.xoffset; + // var b = offsets.bottom + this.yoffset; + // this._elem.css({left:a, bottom:b}); + // break; + // case 'w': + // var a = grid._left + this.xoffset; + // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + // this._elem.css({left:a, top:b}); + // break; + // default: // same as 'se' + // var a = grid._right - this.xoffset; + // var b = grid._bottom + this.yoffset; + // this._elem.css({right:a, bottom:b}); + // break; + // } + // + // } + // else { + // switch (this.location) { + // case 'nw': + // var a = this._plotDimensions.width - grid._left + this.xoffset; + // var b = grid._top + this.yoffset; + // this._elem.css('right', a); + // this._elem.css('top', b); + // break; + // case 'n': + // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + // var b = this._plotDimensions.height - grid._top + this.yoffset; + // this._elem.css('left', a); + // this._elem.css('bottom', b); + // break; + // case 'ne': + // var a = this._plotDimensions.width - offsets.right + this.xoffset; + // var b = grid._top + this.yoffset; + // this._elem.css({left:a, top:b}); + // break; + // case 'e': + // var a = this._plotDimensions.width - offsets.right + this.xoffset; + // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + // this._elem.css({left:a, top:b}); + // break; + // case 'se': + // var a = this._plotDimensions.width - offsets.right + this.xoffset; + // var b = offsets.bottom + this.yoffset; + // this._elem.css({left:a, bottom:b}); + // break; + // case 's': + // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + // var b = this._plotDimensions.height - offsets.bottom + this.yoffset; + // this._elem.css({left:a, top:b}); + // break; + // case 'sw': + // var a = this._plotDimensions.width - grid._left + this.xoffset; + // var b = offsets.bottom + this.yoffset; + // this._elem.css({right:a, bottom:b}); + // break; + // case 'w': + // var a = this._plotDimensions.width - grid._left + this.xoffset; + // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + // this._elem.css({right:a, top:b}); + // break; + // default: // same as 'se' + // var a = grid._right - this.xoffset; + // var b = grid._bottom + this.yoffset; + // this._elem.css({right:a, bottom:b}); + // break; + // } + // } + // } + // }; + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + // only set these if there is a funnel series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.FunnelRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer; + options.legend.renderer = $.jqplot.FunnelLegendRenderer; + options.legend.preDraw = true; + options.sortData = false; + options.seriesDefaults.pointLabels = {show: false}; + } + } + + function postInit(target, data, options) { + // if multiple series, add a reference to the previous one so that + // funnel rings can nest. + for (var i=0; i<this.series.length; i++) { + if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) { + // don't allow mouseover and mousedown at same time. + if (this.series[i].highlightMouseOver) { + this.series[i].highlightMouseDown = false; + } + } + } + } + + // called with scope of plot + function postParseOptions(options) { + for (var i=0; i<this.series.length; i++) { + this.series[i].seriesColors = this.seriesColors; + this.series[i].colorGenerator = $.jqplot.colorGenerator; + } + } + + function highlight (plot, sidx, pidx) { + var s = plot.series[sidx]; + var canvas = plot.plugins.funnelRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx; + s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false); + } + + function unhighlight (plot) { + var canvas = plot.plugins.funnelRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.funnelRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + } + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) { + this.plugins.funnelRenderer.highlightCanvas.resetCanvas(); + this.plugins.funnelRenderer.highlightCanvas = null; + } + + this.plugins.funnelRenderer = {}; + this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + + // do we have any data labels? if so, put highlight canvas before those + var labels = $(this.targetId+' .jqplot-data-label'); + if (labels.length) { + $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); + } + // else put highlight canvas before event canvas. + else { + this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); + } + var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + $.jqplot.preInitHooks.push(preInit); + + $.jqplot.FunnelTickRenderer = function() { + $.jqplot.AxisTickRenderer.call(this); + }; + + $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); + $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer; + +})(jQuery); + + \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.funnelRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.funnelRenderer.min.js new file mode 100644 index 0000000..af0daba --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.funnelRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(e){e.jqplot.FunnelRenderer=function(){e.jqplot.LineRenderer.call(this)};e.jqplot.FunnelRenderer.prototype=new e.jqplot.LineRenderer();e.jqplot.FunnelRenderer.prototype.constructor=e.jqplot.FunnelRenderer;e.jqplot.FunnelRenderer.prototype.init=function(p,t){this.padding={top:20,right:20,bottom:20,left:20};this.sectionMargin=6;this.fill=true;this.shadowOffset=2;this.shadowAlpha=0.07;this.shadowDepth=5;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.widthRatio=0.2;this.lineWidth=2;this.dataLabels="percent";this.showDataLabels=false;this.dataLabelFormatString=null;this.dataLabelThreshold=3;this._type="funnel";this.tickRenderer=e.jqplot.FunnelTickRenderer;if(p.highlightMouseDown&&p.highlightMouseOver==null){p.highlightMouseOver=false}e.extend(true,this,p);this._highlightedPoint=null;this._bases=[];this._atot;this._areas=[];this._lengths=[];this._angle;this._dataIndices=[];this._unorderedData=e.extend(true,[],this.data);var o=e.extend(true,[],this.data);for(var r=0;r<o.length;r++){o[r].push(r)}this.data.sort(function(v,u){return u[1]-v[1]});o.sort(function(v,u){return u[1]-v[1]});for(var r=0;r<o.length;r++){this._dataIndices.push(o[r][2])}if(this.highlightColors.length==0){for(var r=0;r<this.seriesColors.length;r++){var q=e.jqplot.getColorComponents(this.seriesColors[r]);var m=[q[0],q[1],q[2]];var s=m[0]+m[1]+m[2];for(var n=0;n<3;n++){m[n]=(s>570)?m[n]*0.8:m[n]+0.4*(255-m[n]);m[n]=parseInt(m[n],10)}this.highlightColors.push("rgb("+m[0]+","+m[1]+","+m[2]+")")}}t.postParseOptionsHooks.addOnce(k);t.postInitHooks.addOnce(g);t.eventListenerHooks.addOnce("jqplotMouseMove",a);t.eventListenerHooks.addOnce("jqplotMouseDown",b);t.eventListenerHooks.addOnce("jqplotMouseUp",j);t.eventListenerHooks.addOnce("jqplotClick",f);t.eventListenerHooks.addOnce("jqplotRightClick",l);t.postDrawHooks.addOnce(h)};e.jqplot.FunnelRenderer.prototype.setGridData=function(o){var n=0;var p=[];for(var m=0;m<this.data.length;m++){n+=this.data[m][1];p.push([this.data[m][0],this.data[m][1]])}for(var m=0;m<p.length;m++){p[m][1]=p[m][1]/n}this._bases=new Array(p.length+1);this._lengths=new Array(p.length);this.gridData=p};e.jqplot.FunnelRenderer.prototype.makeGridData=function(o,p){var n=0;var q=[];for(var m=0;m<this.data.length;m++){n+=this.data[m][1];q.push([this.data[m][0],this.data[m][1]])}for(var m=0;m<q.length;m++){q[m][1]=q[m][1]/n}this._bases=new Array(q.length+1);this._lengths=new Array(q.length);return q};e.jqplot.FunnelRenderer.prototype.drawSection=function(n,p,o,s){var t=this.fill;var m=this.lineWidth;n.save();if(s){for(var r=0;r<this.shadowDepth;r++){n.save();n.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI),this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));q()}}else{q()}function q(){n.beginPath();n.fillStyle=o;n.strokeStyle=o;n.lineWidth=m;n.moveTo(p[0][0],p[0][1]);for(var u=1;u<4;u++){n.lineTo(p[u][0],p[u][1])}n.closePath();if(t){n.fill()}else{n.stroke()}}if(s){for(var r=0;r<this.shadowDepth;r++){n.restore()}}n.restore()};e.jqplot.FunnelRenderer.prototype.draw=function(G,B,J,p){var Y;var L=(J!=undefined)?J:{};var w=0;var u=0;var R=1;this._areas=[];if(J.legendInfo&&J.legendInfo.placement=="insideGrid"){var O=J.legendInfo;switch(O.location){case"nw":w=O.width+O.xoffset;break;case"w":w=O.width+O.xoffset;break;case"sw":w=O.width+O.xoffset;break;case"ne":w=O.width+O.xoffset;R=-1;break;case"e":w=O.width+O.xoffset;R=-1;break;case"se":w=O.width+O.xoffset;R=-1;break;case"n":u=O.height+O.yoffset;break;case"s":u=O.height+O.yoffset;R=-1;break;default:break}}var t=(R==1)?this.padding.left+w:this.padding.left;var F=(R==1)?this.padding.top+u:this.padding.top;var M=(R==-1)?this.padding.right+w:this.padding.right;var o=(R==-1)?this.padding.bottom+u:this.padding.bottom;var P=(L.shadow!=undefined)?L.shadow:this.shadow;var q=(L.showLine!=undefined)?L.showLine:this.showLine;var C=(L.fill!=undefined)?L.fill:this.fill;var H=G.canvas.width;var N=G.canvas.height;this._bases[0]=H-t-M;var I=this._length=N-F-o;var r=this._bases[0]*this.widthRatio;this._atot=I/2*(this._bases[0]+this._bases[0]*this.widthRatio);this._angle=Math.atan((this._bases[0]-r)/2/I);for(Y=0;Y<B.length;Y++){this._areas.push(B[Y][1]*this._atot)}var E,aa,W,Q=0;var n=0.0001;for(Y=0;Y<this._areas.length;Y++){E=this._areas[Y]/this._bases[Y];aa=999999;this._lengths[Y]=E;W=0;while(aa>this._lengths[Y]*n&&W<100){this._lengths[Y]=this._areas[Y]/(this._bases[Y]-this._lengths[Y]*Math.tan(this._angle));aa=Math.abs(this._lengths[Y]-E);this._bases[Y+1]=this._bases[Y]-(2*this._lengths[Y]*Math.tan(this._angle));E=this._lengths[Y];W++}Q+=this._lengths[Y]}this._vertices=new Array(B.length);var ae=[t,F],ad=[t+this._bases[0],F],ac=[t+(this._bases[0]-this._bases[this._bases.length-1])/2,F+this._length],ab=[ac[0]+this._bases[this._bases.length-1],ac[1]];function V(ag){var x=(ae[1]-ac[1])/(ae[0]-ac[0]);var v=ae[1]-x*ae[0];var ah=ag+ae[1];return[(ah-v)/x,ah]}function D(ag){var x=(ad[1]-ab[1])/(ad[0]-ab[0]);var v=ad[1]-x*ad[0];var ah=ag+ad[1];return[(ah-v)/x,ah]}var T=w,S=u;var Z=0,m=0;for(Y=0;Y<B.length;Y++){this._vertices[Y]=new Array();var U=this._vertices[Y];var A=this.sectionMargin;if(Y==0){m=0}if(Y==1){m=A/3}else{if(Y>0&&Y<B.length-1){m=A/2}else{if(Y==B.length-1){m=2*A/3}}}U.push(V(Z+m));U.push(D(Z+m));Z+=this._lengths[Y];if(Y==0){m=-2*A/3}else{if(Y>0&&Y<B.length-1){m=-A/2}else{if(Y==B.length-1){m=0}}}U.push(D(Z+m));U.push(V(Z+m))}if(this.shadow){var af="rgba(0,0,0,"+this.shadowAlpha+")";for(var Y=0;Y<B.length;Y++){this.renderer.drawSection.call(this,G,this._vertices[Y],af,true)}}for(var Y=0;Y<B.length;Y++){var U=this._vertices[Y];this.renderer.drawSection.call(this,G,U,this.seriesColors[Y]);if(this.showDataLabels&&B[Y][1]*100>=this.dataLabelThreshold){var K,X;if(this.dataLabels=="label"){K=this.dataLabelFormatString||"%s";X=e.jqplot.sprintf(K,B[Y][0])}else{if(this.dataLabels=="value"){K=this.dataLabelFormatString||"%d";X=e.jqplot.sprintf(K,this.data[Y][1])}else{if(this.dataLabels=="percent"){K=this.dataLabelFormatString||"%d%%";X=e.jqplot.sprintf(K,B[Y][1]*100)}else{if(this.dataLabels.constructor==Array){K=this.dataLabelFormatString||"%s";X=e.jqplot.sprintf(K,this.dataLabels[this._dataIndices[Y]])}}}}var s=(this._radius)*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var T=(U[0][0]+U[1][0])/2+this.canvas._offsets.left;var S=(U[1][1]+U[2][1])/2+this.canvas._offsets.top;var z=e('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">'+X+"</span>").insertBefore(p.eventCanvas._elem);T-=z.width()/2;S-=z.height()/2;T=Math.round(T);S=Math.round(S);z.css({left:T,top:S})}}};e.jqplot.FunnelAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.FunnelAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.FunnelAxisRenderer.prototype.constructor=e.jqplot.FunnelAxisRenderer;e.jqplot.FunnelAxisRenderer.prototype.init=function(m){this.tickRenderer=e.jqplot.FunnelTickRenderer;e.extend(true,this,m);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};e.jqplot.FunnelLegendRenderer=function(){e.jqplot.TableLegendRenderer.call(this)};e.jqplot.FunnelLegendRenderer.prototype=new e.jqplot.TableLegendRenderer();e.jqplot.FunnelLegendRenderer.prototype.constructor=e.jqplot.FunnelLegendRenderer;e.jqplot.FunnelLegendRenderer.prototype.init=function(m){this.numberRows=null;this.numberColumns=null;e.extend(true,this,m)};e.jqplot.FunnelLegendRenderer.prototype.draw=function(){var p=this;if(this.show){var x=this._series;var A="position:absolute;";A+=(this.background)?"background:"+this.background+";":"";A+=(this.border)?"border:"+this.border+";":"";A+=(this.fontSize)?"font-size:"+this.fontSize+";":"";A+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";A+=(this.textColor)?"color:"+this.textColor+";":"";A+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";A+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";A+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";A+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=e('<table class="jqplot-table-legend" style="'+A+'"></table>');var E=false,w=false,m,u;var y=x[0];var n=new e.jqplot.ColorGenerator(y.seriesColors);if(y.show){var F=y.data;if(this.numberRows){m=this.numberRows;if(!this.numberColumns){u=Math.ceil(F.length/m)}else{u=this.numberColumns}}else{if(this.numberColumns){u=this.numberColumns;m=Math.ceil(F.length/this.numberColumns)}else{m=F.length;u=1}}var D,C,o,r,q,t,v,B;var z=0;for(D=0;D<m;D++){if(w){o=e('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem)}else{o=e('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem)}for(C=0;C<u;C++){if(z<F.length){t=this.labels[z]||F[z][0].toString();B=n.next();if(!w){if(D>0){E=true}else{E=false}}else{if(D==m-1){E=false}else{E=true}}v=(E)?this.rowSpacing:"0";r=e('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+v+';"><div><div class="jqplot-table-legend-swatch" style="border-color:'+B+';"></div></div></td>');q=e('<td class="jqplot-table-legend" style="padding-top:'+v+';"></td>');if(this.escapeHtml){q.text(t)}else{q.html(t)}if(w){q.prependTo(o);r.prependTo(o)}else{r.appendTo(o);q.appendTo(o)}E=true}z++}}}}return this._elem};function c(q,p,n){n=n||{};n.axesDefaults=n.axesDefaults||{};n.legend=n.legend||{};n.seriesDefaults=n.seriesDefaults||{};var m=false;if(n.seriesDefaults.renderer==e.jqplot.FunnelRenderer){m=true}else{if(n.series){for(var o=0;o<n.series.length;o++){if(n.series[o].renderer==e.jqplot.FunnelRenderer){m=true}}}}if(m){n.axesDefaults.renderer=e.jqplot.FunnelAxisRenderer;n.legend.renderer=e.jqplot.FunnelLegendRenderer;n.legend.preDraw=true;n.sortData=false;n.seriesDefaults.pointLabels={show:false}}}function g(p,o,m){for(var n=0;n<this.series.length;n++){if(this.series[n].renderer.constructor==e.jqplot.FunnelRenderer){if(this.series[n].highlightMouseOver){this.series[n].highlightMouseDown=false}}}}function k(m){for(var n=0;n<this.series.length;n++){this.series[n].seriesColors=this.seriesColors;this.series[n].colorGenerator=e.jqplot.colorGenerator}}function d(q,p,o){var n=q.series[p];var m=q.plugins.funnelRenderer.highlightCanvas;m._ctx.clearRect(0,0,m._ctx.canvas.width,m._ctx.canvas.height);n._highlightedPoint=o;q.plugins.funnelRenderer.highlightedSeriesIndex=p;n.renderer.drawSection.call(n,m._ctx,n._vertices[o],n.highlightColors[o],false)}function i(o){var m=o.plugins.funnelRenderer.highlightCanvas;m._ctx.clearRect(0,0,m._ctx.canvas.width,m._ctx.canvas.height);for(var n=0;n<o.series.length;n++){o.series[n]._highlightedPoint=null}o.plugins.funnelRenderer.highlightedSeriesIndex=null;o.target.trigger("jqplotDataUnhighlight")}function a(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];var n=jQuery.Event("jqplotDataMouseOver");n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o);if(r.series[o[0]].highlightMouseOver&&!(o[0]==r.plugins.funnelRenderer.highlightedSeriesIndex&&o[1]==r.series[o[0]]._highlightedPoint)){var m=jQuery.Event("jqplotDataHighlight");m.which=q.which;m.pageX=q.pageX;m.pageY=q.pageY;r.target.trigger(m,o);d(r,o[0],o[1])}}else{if(s==null){i(r)}}}function b(p,o,s,r,q){if(r){var n=[r.seriesIndex,r.pointIndex,r.data];if(q.series[n[0]].highlightMouseDown&&!(n[0]==q.plugins.funnelRenderer.highlightedSeriesIndex&&n[1]==q.series[n[0]]._highlightedPoint)){var m=jQuery.Event("jqplotDataHighlight");m.which=p.which;m.pageX=p.pageX;m.pageY=p.pageY;q.target.trigger(m,n);d(q,n[0],n[1])}}else{if(r==null){i(q)}}}function j(o,n,r,q,p){var m=p.plugins.funnelRenderer.highlightedSeriesIndex;if(m!=null&&p.series[m].highlightMouseDown){i(p)}}function f(p,o,s,r,q){if(r){var n=[r.seriesIndex,r.pointIndex,r.data];var m=jQuery.Event("jqplotDataClick");m.which=p.which;m.pageX=p.pageX;m.pageY=p.pageY;q.target.trigger(m,n)}}function l(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];var m=r.plugins.funnelRenderer.highlightedSeriesIndex;if(m!=null&&r.series[m].highlightMouseDown){i(r)}var n=jQuery.Event("jqplotDataRightClick");n.which=q.which;n.pageX=q.pageX;n.pageY=q.pageY;r.target.trigger(n,o)}}function h(){if(this.plugins.funnelRenderer&&this.plugins.funnelRenderer.highlightCanvas){this.plugins.funnelRenderer.highlightCanvas.resetCanvas();this.plugins.funnelRenderer.highlightCanvas=null}this.plugins.funnelRenderer={};this.plugins.funnelRenderer.highlightCanvas=new e.jqplot.GenericCanvas();var n=e(this.targetId+" .jqplot-data-label");if(n.length){e(n[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-funnelRenderer-highlight-canvas",this._plotDimensions,this))}else{this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-funnelRenderer-highlight-canvas",this._plotDimensions,this))}var m=this.plugins.funnelRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(o){i(o.data.plot)})}e.jqplot.preInitHooks.push(c);e.jqplot.FunnelTickRenderer=function(){e.jqplot.AxisTickRenderer.call(this)};e.jqplot.FunnelTickRenderer.prototype=new e.jqplot.AxisTickRenderer();e.jqplot.FunnelTickRenderer.prototype.constructor=e.jqplot.FunnelTickRenderer})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.highlighter.js b/web/extensions/jqplot/plugins/jqplot.highlighter.js new file mode 100644 index 0000000..3105c85 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.highlighter.js @@ -0,0 +1,465 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); + + /** + * Class: $.jqplot.Highlighter + * Plugin which will highlight data points when they are moused over. + * + * To use this plugin, include the js + * file in your source: + * + * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script> + * + * A tooltip providing information about the data point is enabled by default. + * To disable the tooltip, set "showTooltip" to false. + * + * You can control what data is displayed in the tooltip with various + * options. The "tooltipAxes" option controls whether the x, y or both + * data values are displayed. + * + * Some chart types (e.g. hi-low-close) have more than one y value per + * data point. To display the additional values in the tooltip, set the + * "yvalues" option to the desired number of y values present (3 for a hlc chart). + * + * By default, data values will be formatted with the same formatting + * specifiers as used to format the axis ticks. A custom format code + * can be supplied with the tooltipFormatString option. This will apply + * to all values in the tooltip. + * + * For more complete control, the "formatString" option can be set. This + * Allows conplete control over tooltip formatting. Values are passed to + * the format string in an order determined by the "tooltipAxes" and "yvalues" + * options. So, if you have a hi-low-close chart and you just want to display + * the hi-low-close values in the tooltip, you could set a formatString like: + * + * > highlighter: { + * > tooltipAxes: 'y', + * > yvalues: 3, + * > formatString:'<table class="jqplot-highlighter"> + * > <tr><td>hi:</td><td>%s</td></tr> + * > <tr><td>low:</td><td>%s</td></tr> + * > <tr><td>close:</td><td>%s</td></tr></table>' + * > } + * + */ + $.jqplot.Highlighter = function(options) { + // Group: Properties + // + //prop: show + // true to show the highlight. + this.show = $.jqplot.config.enablePlugins; + // prop: markerRenderer + // Renderer used to draw the marker of the highlighted point. + // Renderer will assimilate attributes from the data point being highlighted, + // so no attributes need set on the renderer directly. + // Default is to turn off shadow drawing on the highlighted point. + this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); + // prop: showMarker + // true to show the marker + this.showMarker = true; + // prop: lineWidthAdjust + // Pixels to add to the lineWidth of the highlight. + this.lineWidthAdjust = 2.5; + // prop: sizeAdjust + // Pixels to add to the overall size of the highlight. + this.sizeAdjust = 5; + // prop: showTooltip + // Show a tooltip with data point values. + this.showTooltip = true; + // prop: tooltipLocation + // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' + this.tooltipLocation = 'nw'; + // prop: fadeTooltip + // true = fade in/out tooltip, flase = show/hide tooltip + this.fadeTooltip = true; + // prop: tooltipFadeSpeed + // 'slow', 'def', 'fast', or number of milliseconds. + this.tooltipFadeSpeed = "fast"; + // prop: tooltipOffset + // Pixel offset of tooltip from the highlight. + this.tooltipOffset = 2; + // prop: tooltipAxes + // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx' + // 'both' and 'xy' are equivalent, 'yx' reverses order of labels. + this.tooltipAxes = 'both'; + // prop; tooltipSeparator + // String to use to separate x and y axes in tooltip. + this.tooltipSeparator = ', '; + // prop; tooltipContentEditor + // Function used to edit/augment/replace the formatted tooltip contents. + // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex) + // where str is the generated tooltip html and seriesIndex and pointIndex identify + // the data point being highlighted. Should return the html for the tooltip contents. + this.tooltipContentEditor = null; + // prop: useAxesFormatters + // Use the x and y axes formatters to format the text in the tooltip. + this.useAxesFormatters = true; + // prop: tooltipFormatString + // sprintf format string for the tooltip. + // Uses Ash Searle's javascript sprintf implementation + // found here: http://hexmen.com/blog/2007/03/printf-sprintf/ + // See http://perldoc.perl.org/functions/sprintf.html for reference. + // Additional "p" and "P" format specifiers added by Chris Leonello. + this.tooltipFormatString = '%.5P'; + // prop: formatString + // alternative to tooltipFormatString + // will format the whole tooltip text, populating with x, y values as + // indicated by tooltipAxes option. So, you could have a tooltip like: + // 'Date: %s, number of cats: %d' to format the whole tooltip at one go. + // If useAxesFormatters is true, values will be formatted according to + // Axes formatters and you can populate your tooltip string with + // %s placeholders. + this.formatString = null; + // prop: yvalues + // Number of y values to expect in the data point array. + // Typically this is 1. Certain plots, like OHLC, will + // have more y values in each data point array. + this.yvalues = 1; + // prop: bringSeriesToFront + // This option requires jQuery 1.4+ + // True to bring the series of the highlighted point to the front + // of other series. + this.bringSeriesToFront = false; + this._tooltipElem; + this.isHighlighting = false; + this.currentNeighbor = null; + + $.extend(true, this, options); + }; + + var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; + var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; + var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; + + // axis.renderer.tickrenderer.formatter + + // called with scope of plot + $.jqplot.Highlighter.init = function (target, data, opts){ + var options = opts || {}; + // add a highlighter attribute to the plot + this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter); + }; + + // called within scope of series + $.jqplot.Highlighter.parseOptions = function (defaults, options) { + // Add a showHighlight option to the series + // and set it to true by default. + this.showHighlight = true; + }; + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + $.jqplot.Highlighter.postPlotDraw = function() { + // Memory Leaks patch + if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) { + this.plugins.highlighter.highlightCanvas.resetCanvas(); + this.plugins.highlighter.highlightCanvas = null; + } + + if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) { + this.plugins.highlighter._tooltipElem.emptyForce(); + this.plugins.highlighter._tooltipElem = null; + } + + this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this)); + this.plugins.highlighter.highlightCanvas.setContext(); + + var elem = document.createElement('div'); + this.plugins.highlighter._tooltipElem = $(elem); + elem = null; + this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip'); + this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'}); + + this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem); + }; + + $.jqplot.preInitHooks.push($.jqplot.Highlighter.init); + $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions); + $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw); + + function draw(plot, neighbor) { + var hl = plot.plugins.highlighter; + var s = plot.series[neighbor.seriesIndex]; + var smr = s.markerRenderer; + var mr = hl.markerRenderer; + mr.style = smr.style; + mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust; + mr.size = smr.size + hl.sizeAdjust; + var rgba = $.jqplot.getColorComponents(smr.color); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); + mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; + mr.init(); + mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx); + } + + function showTooltip(plot, series, neighbor) { + // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]} + // gridData should be x,y pixel coords on the grid. + // add the plot._gridPadding to that to get x,y in the target. + var hl = plot.plugins.highlighter; + var elem = hl._tooltipElem; + var serieshl = series.highlighter || {}; + + var opts = $.extend(true, {}, hl, serieshl); + + if (opts.useAxesFormatters) { + var xf = series._xaxis._ticks[0].formatter; + var yf = series._yaxis._ticks[0].formatter; + var xfstr = series._xaxis._ticks[0].formatString; + var yfstr = series._yaxis._ticks[0].formatString; + var str; + var xstr = xf(xfstr, neighbor.data[0]); + var ystrs = []; + for (var i=1; i<opts.yvalues+1; i++) { + ystrs.push(yf(yfstr, neighbor.data[i])); + } + if (typeof opts.formatString === 'string') { + switch (opts.tooltipAxes) { + case 'both': + case 'xy': + ystrs.unshift(xstr); + ystrs.unshift(opts.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + case 'yx': + ystrs.push(xstr); + ystrs.unshift(opts.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + case 'x': + str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]); + break; + case 'y': + ystrs.unshift(opts.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + default: // same as xy + ystrs.unshift(xstr); + ystrs.unshift(opts.formatString); + str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); + break; + } + } + else { + switch (opts.tooltipAxes) { + case 'both': + case 'xy': + str = xstr; + for (var i=0; i<ystrs.length; i++) { + str += opts.tooltipSeparator + ystrs[i]; + } + break; + case 'yx': + str = ''; + for (var i=0; i<ystrs.length; i++) { + str += ystrs[i] + opts.tooltipSeparator; + } + str += xstr; + break; + case 'x': + str = xstr; + break; + case 'y': + str = ystrs.join(opts.tooltipSeparator); + break; + default: // same as 'xy' + str = xstr; + for (var i=0; i<ystrs.length; i++) { + str += opts.tooltipSeparator + ystrs[i]; + } + break; + + } + } + } + else { + var str; + if (typeof opts.formatString === 'string') { + str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data)); + } + + else { + if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') { + str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); + } + else if (opts.tooltipAxes == 'yx') { + str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); + } + else if (opts.tooltipAxes == 'x') { + str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); + } + else if (opts.tooltipAxes == 'y') { + str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); + } + } + } + if ($.isFunction(opts.tooltipContentEditor)) { + // args str, seriesIndex, pointIndex are essential so the hook can look up + // extra data for the point. + str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot); + } + elem.html(str); + var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]}; + var ms = 0; + var fact = 0.707; + if (series.markerRenderer.show == true) { + ms = (series.markerRenderer.size + opts.sizeAdjust)/2; + } + + var loc = locations; + if (series.fillToZero && series.fill && neighbor.data[1] < 0) { + loc = oppositeLocations; + } + + switch (loc[locationIndicies[opts.tooltipLocation]]) { + case 'nw': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; + var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; + break; + case 'n': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; + var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms; + break; + case 'ne': + var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; + var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; + break; + case 'e': + var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms; + var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + case 'se': + var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; + var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; + break; + case 's': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; + var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms; + break; + case 'sw': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; + var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; + break; + case 'w': + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms; + var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; + break; + default: // same as 'nw' + var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; + var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; + break; + } + elem.css('left', x); + elem.css('top', y); + if (opts.fadeTooltip) { + // Fix for stacked up animations. Thnanks Trevor! + elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed); + } + else { + elem.show(); + } + elem = null; + + } + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + var hl = plot.plugins.highlighter; + var c = plot.plugins.cursor; + if (hl.show) { + if (neighbor == null && hl.isHighlighting) { + var evt = jQuery.Event('jqplotHighlighterUnhighlight'); + plot.target.trigger(evt); + + var ctx = hl.highlightCanvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + if (hl.fadeTooltip) { + hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed); + } + else { + hl._tooltipElem.hide(); + } + if (hl.bringSeriesToFront) { + plot.restorePreviousSeriesOrder(); + } + hl.isHighlighting = false; + hl.currentNeighbor = null; + ctx = null; + } + else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) { + var evt = jQuery.Event('jqplotHighlighterHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot]; + plot.target.trigger(evt, ins); + + hl.isHighlighting = true; + hl.currentNeighbor = neighbor; + if (hl.showMarker) { + draw(plot, neighbor); + } + if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { + showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); + } + if (hl.bringSeriesToFront) { + plot.moveSeriesToFront(neighbor.seriesIndex); + } + } + // check to see if we're highlighting the wrong point. + else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) { + // highlighting the wrong point. + + // if new series allows highlighting, highlight new point. + if (plot.series[neighbor.seriesIndex].showHighlight) { + var ctx = hl.highlightCanvas._ctx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + hl.isHighlighting = true; + hl.currentNeighbor = neighbor; + if (hl.showMarker) { + draw(plot, neighbor); + } + if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { + showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); + } + if (hl.bringSeriesToFront) { + plot.moveSeriesToFront(neighbor.seriesIndex); + } + } + } + } + } +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.highlighter.min.js b/web/extensions/jqplot/plugins/jqplot.highlighter.min.js new file mode 100644 index 0000000..1e0f6e5 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.highlighter.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(d){d.jqplot.eventListenerHooks.push(["jqplotMouseMove",f]);d.jqplot.Highlighter=function(h){this.show=d.jqplot.config.enablePlugins;this.markerRenderer=new d.jqplot.MarkerRenderer({shadow:false});this.showMarker=true;this.lineWidthAdjust=2.5;this.sizeAdjust=5;this.showTooltip=true;this.tooltipLocation="nw";this.fadeTooltip=true;this.tooltipFadeSpeed="fast";this.tooltipOffset=2;this.tooltipAxes="both";this.tooltipSeparator=", ";this.tooltipContentEditor=null;this.useAxesFormatters=true;this.tooltipFormatString="%.5P";this.formatString=null;this.yvalues=1;this.bringSeriesToFront=false;this._tooltipElem;this.isHighlighting=false;this.currentNeighbor=null;d.extend(true,this,h)};var b=["nw","n","ne","e","se","s","sw","w"];var e={nw:0,n:1,ne:2,e:3,se:4,s:5,sw:6,w:7};var c=["se","s","sw","w","nw","n","ne","e"];d.jqplot.Highlighter.init=function(k,j,i){var h=i||{};this.plugins.highlighter=new d.jqplot.Highlighter(h.highlighter)};d.jqplot.Highlighter.parseOptions=function(i,h){this.showHighlight=true};d.jqplot.Highlighter.postPlotDraw=function(){if(this.plugins.highlighter&&this.plugins.highlighter.highlightCanvas){this.plugins.highlighter.highlightCanvas.resetCanvas();this.plugins.highlighter.highlightCanvas=null}if(this.plugins.highlighter&&this.plugins.highlighter._tooltipElem){this.plugins.highlighter._tooltipElem.emptyForce();this.plugins.highlighter._tooltipElem=null}this.plugins.highlighter.highlightCanvas=new d.jqplot.GenericCanvas();this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding,"jqplot-highlight-canvas",this._plotDimensions,this));this.plugins.highlighter.highlightCanvas.setContext();var h=document.createElement("div");this.plugins.highlighter._tooltipElem=d(h);h=null;this.plugins.highlighter._tooltipElem.addClass("jqplot-highlighter-tooltip");this.plugins.highlighter._tooltipElem.css({position:"absolute",display:"none"});this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem)};d.jqplot.preInitHooks.push(d.jqplot.Highlighter.init);d.jqplot.preParseSeriesOptionsHooks.push(d.jqplot.Highlighter.parseOptions);d.jqplot.postDrawHooks.push(d.jqplot.Highlighter.postPlotDraw);function a(m,o){var j=m.plugins.highlighter;var p=m.series[o.seriesIndex];var h=p.markerRenderer;var i=j.markerRenderer;i.style=h.style;i.lineWidth=h.lineWidth+j.lineWidthAdjust;i.size=h.size+j.sizeAdjust;var l=d.jqplot.getColorComponents(h.color);var n=[l[0],l[1],l[2]];var k=(l[3]>=0.6)?l[3]*0.6:l[3]*(2-l[3]);i.color="rgba("+n[0]+","+n[1]+","+n[2]+","+k+")";i.init();i.draw(p.gridData[o.pointIndex][0],p.gridData[o.pointIndex][1],j.highlightCanvas._ctx)}function g(A,q,m){var k=A.plugins.highlighter;var D=k._tooltipElem;var r=q.highlighter||{};var t=d.extend(true,{},k,r);if(t.useAxesFormatters){var w=q._xaxis._ticks[0].formatter;var h=q._yaxis._ticks[0].formatter;var E=q._xaxis._ticks[0].formatString;var s=q._yaxis._ticks[0].formatString;var z;var u=w(E,m.data[0]);var l=[];for(var B=1;B<t.yvalues+1;B++){l.push(h(s,m.data[B]))}if(typeof t.formatString==="string"){switch(t.tooltipAxes){case"both":case"xy":l.unshift(u);l.unshift(t.formatString);z=d.jqplot.sprintf.apply(d.jqplot.sprintf,l);break;case"yx":l.push(u);l.unshift(t.formatString);z=d.jqplot.sprintf.apply(d.jqplot.sprintf,l);break;case"x":z=d.jqplot.sprintf.apply(d.jqplot.sprintf,[t.formatString,u]);break;case"y":l.unshift(t.formatString);z=d.jqplot.sprintf.apply(d.jqplot.sprintf,l);break;default:l.unshift(u);l.unshift(t.formatString);z=d.jqplot.sprintf.apply(d.jqplot.sprintf,l);break}}else{switch(t.tooltipAxes){case"both":case"xy":z=u;for(var B=0;B<l.length;B++){z+=t.tooltipSeparator+l[B]}break;case"yx":z="";for(var B=0;B<l.length;B++){z+=l[B]+t.tooltipSeparator}z+=u;break;case"x":z=u;break;case"y":z=l.join(t.tooltipSeparator);break;default:z=u;for(var B=0;B<l.length;B++){z+=t.tooltipSeparator+l[B]}break}}}else{var z;if(typeof t.formatString==="string"){z=d.jqplot.sprintf.apply(d.jqplot.sprintf,[t.formatString].concat(m.data))}else{if(t.tooltipAxes=="both"||t.tooltipAxes=="xy"){z=d.jqplot.sprintf(t.tooltipFormatString,m.data[0])+t.tooltipSeparator+d.jqplot.sprintf(t.tooltipFormatString,m.data[1])}else{if(t.tooltipAxes=="yx"){z=d.jqplot.sprintf(t.tooltipFormatString,m.data[1])+t.tooltipSeparator+d.jqplot.sprintf(t.tooltipFormatString,m.data[0])}else{if(t.tooltipAxes=="x"){z=d.jqplot.sprintf(t.tooltipFormatString,m.data[0])}else{if(t.tooltipAxes=="y"){z=d.jqplot.sprintf(t.tooltipFormatString,m.data[1])}}}}}}if(d.isFunction(t.tooltipContentEditor)){z=t.tooltipContentEditor(z,m.seriesIndex,m.pointIndex,A)}D.html(z);var C={x:m.gridData[0],y:m.gridData[1]};var v=0;var j=0.707;if(q.markerRenderer.show==true){v=(q.markerRenderer.size+t.sizeAdjust)/2}var o=b;if(q.fillToZero&&q.fill&&m.data[1]<0){o=c}switch(o[e[t.tooltipLocation]]){case"nw":var p=C.x+A._gridPadding.left-D.outerWidth(true)-t.tooltipOffset-j*v;var n=C.y+A._gridPadding.top-t.tooltipOffset-D.outerHeight(true)-j*v;break;case"n":var p=C.x+A._gridPadding.left-D.outerWidth(true)/2;var n=C.y+A._gridPadding.top-t.tooltipOffset-D.outerHeight(true)-v;break;case"ne":var p=C.x+A._gridPadding.left+t.tooltipOffset+j*v;var n=C.y+A._gridPadding.top-t.tooltipOffset-D.outerHeight(true)-j*v;break;case"e":var p=C.x+A._gridPadding.left+t.tooltipOffset+v;var n=C.y+A._gridPadding.top-D.outerHeight(true)/2;break;case"se":var p=C.x+A._gridPadding.left+t.tooltipOffset+j*v;var n=C.y+A._gridPadding.top+t.tooltipOffset+j*v;break;case"s":var p=C.x+A._gridPadding.left-D.outerWidth(true)/2;var n=C.y+A._gridPadding.top+t.tooltipOffset+v;break;case"sw":var p=C.x+A._gridPadding.left-D.outerWidth(true)-t.tooltipOffset-j*v;var n=C.y+A._gridPadding.top+t.tooltipOffset+j*v;break;case"w":var p=C.x+A._gridPadding.left-D.outerWidth(true)-t.tooltipOffset-v;var n=C.y+A._gridPadding.top-D.outerHeight(true)/2;break;default:var p=C.x+A._gridPadding.left-D.outerWidth(true)-t.tooltipOffset-j*v;var n=C.y+A._gridPadding.top-t.tooltipOffset-D.outerHeight(true)-j*v;break}D.css("left",p);D.css("top",n);if(t.fadeTooltip){D.stop(true,true).fadeIn(t.tooltipFadeSpeed)}else{D.show()}D=null}function f(n,j,i,p,l){var h=l.plugins.highlighter;var m=l.plugins.cursor;if(h.show){if(p==null&&h.isHighlighting){var o=jQuery.Event("jqplotHighlighterUnhighlight");l.target.trigger(o);var q=h.highlightCanvas._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);if(h.fadeTooltip){h._tooltipElem.fadeOut(h.tooltipFadeSpeed)}else{h._tooltipElem.hide()}if(h.bringSeriesToFront){l.restorePreviousSeriesOrder()}h.isHighlighting=false;h.currentNeighbor=null;q=null}else{if(p!=null&&l.series[p.seriesIndex].showHighlight&&!h.isHighlighting){var o=jQuery.Event("jqplotHighlighterHighlight");o.which=n.which;o.pageX=n.pageX;o.pageY=n.pageY;var k=[p.seriesIndex,p.pointIndex,p.data,l];l.target.trigger(o,k);h.isHighlighting=true;h.currentNeighbor=p;if(h.showMarker){a(l,p)}if(l.series[p.seriesIndex].show&&h.showTooltip&&(!m||!m._zoom.started)){g(l,l.series[p.seriesIndex],p)}if(h.bringSeriesToFront){l.moveSeriesToFront(p.seriesIndex)}}else{if(p!=null&&h.isHighlighting&&h.currentNeighbor!=p){if(l.series[p.seriesIndex].showHighlight){var q=h.highlightCanvas._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);h.isHighlighting=true;h.currentNeighbor=p;if(h.showMarker){a(l,p)}if(l.series[p.seriesIndex].show&&h.showTooltip&&(!m||!m._zoom.started)){g(l,l.series[p.seriesIndex],p)}if(h.bringSeriesToFront){l.moveSeriesToFront(p.seriesIndex)}}}}}}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.json2.js b/web/extensions/jqplot/plugins/jqplot.json2.js new file mode 100644 index 0000000..46fb942 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.json2.js @@ -0,0 +1,475 @@ +/* + 2010-11-01 Chris Leonello + + Slightly modified version of the original json2.js to put JSON + functions under the $.jqplot namespace. + + licensing and orignal comments follow: + + http://www.JSON.org/json2.js + 2010-08-25 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + $.jqplot.JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + $.jqplot.JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = $.jqplot.JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + $.jqplot.JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = $.jqplot.JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = $.jqplot.JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +(function($) { + + $.jqplot.JSON = window.JSON; + + if (!window.JSON) { + $.jqplot.JSON = {}; + } + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof $.jqplot.JSON.stringify !== 'function') { + $.jqplot.JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('$.jqplot.JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof $.jqplot.JSON.parse !== 'function') { + $.jqplot.JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('$.jqplot.JSON.parse'); + }; + } +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.json2.min.js b/web/extensions/jqplot/plugins/jqplot.json2.min.js new file mode 100644 index 0000000..3d8c626 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.json2.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function($){$.jqplot.JSON=window.JSON;if(!window.JSON){$.jqplot.JSON={}}function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof $.jqplot.JSON.stringify!=="function"){$.jqplot.JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("$.jqplot.JSON.stringify")}return str("",{"":value})}}if(typeof $.jqplot.JSON.parse!=="function"){$.jqplot.JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("$.jqplot.JSON.parse")}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.js b/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.js new file mode 100644 index 0000000..3bd3e2a --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.js @@ -0,0 +1,534 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * class: $.jqplot.LogAxisRenderer + * A plugin for a jqPlot to render a logarithmic axis. + * + * To use this renderer, include the plugin in your source + * > <script type="text/javascript" language="javascript" src="plugins/jqplot.logAxisRenderer.js"></script> + * + * and supply the appropriate options to your plot + * + * > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}} + **/ + $.jqplot.LogAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + // prop: axisDefaults + // Default properties which will be applied directly to the series. + // + // Group: Properties + // + // Properties + // + // base - the logarithmic base, commonly 2, 10 or Math.E + // tickDistribution - Deprecated. "power" distribution of ticks + // always used. Option has no effect. + this.axisDefaults = { + base : 10, + tickDistribution :'power' + }; + }; + + $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer; + + $.jqplot.LogAxisRenderer.prototype.init = function(options) { + // prop: drawBaseline + // True to draw the axis baseline. + this.drawBaseline = true; + // prop: minorTicks + // Number of ticks to add between "major" ticks. + // Major ticks are ticks supplied by user or auto computed. + // Minor ticks cannot be created by user. + this.minorTicks = 'auto'; + this._scalefact = 1.0; + + $.extend(true, this, options); + + this._autoFormatString = '%d'; + this._overrideFormatString = false; + + for (var d in this.renderer.axisDefaults) { + if (this[d] == null) { + this[d] = this.renderer.axisDefaults[d]; + } + } + + this.resetDataBounds(); + }; + + $.jqplot.LogAxisRenderer.prototype.createTicks = function(plot) { + // we're are operating on an axis here + var ticks = this._ticks; + var userTicks = this.ticks; + var name = this.name; + var db = this._dataBounds; + var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; + var interval; + var min, max; + var pos1, pos2; + var tt, i; + + var threshold = 30; + // For some reason scalefactor is screwing up ticks. + this._scalefact = (Math.max(dim, threshold+1) - threshold)/300; + + // if we already have ticks, use them. + // ticks must be in order of increasing value. + if (userTicks.length) { + // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed + for (i=0; i<userTicks.length; i++){ + var ut = userTicks[i]; + var t = new this.tickRenderer(this.tickOptions); + if (ut.constructor == Array) { + t.value = ut[0]; + t.label = ut[1]; + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(ut[0], this.name); + this._ticks.push(t); + } + + else if ($.isPlainObject(ut)) { + $.extend(true, t, ut); + t.axis = this.name; + this._ticks.push(t); + } + + else { + t.value = ut; + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(ut, this.name); + this._ticks.push(t); + } + } + this.numberTicks = userTicks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this.numberTicks-1].value; + } + + // we don't have any ticks yet, let's make some! + else if (this.min == null && this.max == null) { + min = db.min * (2 - this.padMin); + max = db.max * this.padMax; + + // if min and max are same, space them out a bit + if (min == max) { + var adj = 0.05; + min = min*(1-adj); + max = max*(1+adj); + } + + // perform some checks + if (this.min != null && this.min <= 0) { + throw new Error("Log axis minimum must be greater than 0"); + } + if (this.max != null && this.max <= 0) { + throw new Error("Log axis maximum must be greater than 0"); + } + + function findCeil (val) { + var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10)); + return Math.ceil(val/order) * order; + } + + function findFloor(val) { + var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10)); + return Math.floor(val/order) * order; + } + + // var range = max - min; + var rmin, rmax; + + // for power distribution, open up range to get a nice power of axis.renderer.base. + // power distribution won't respect the user's min/max settings. + rmin = Math.pow(this.base, Math.floor(Math.log(min)/Math.log(this.base))); + rmax = Math.pow(this.base, Math.ceil(Math.log(max)/Math.log(this.base))); + + // // if min and max are same, space them out a bit + // if (rmin === rmax) { + // var adj = 0.05; + // rmin = rmin*(1-adj); + // rmax = rmax*(1+adj); + // } + + // Handle case where a data value was zero + if (rmin === 0) { + rmin = 1; + } + + var order = Math.round(Math.log(rmin)/Math.LN10); + + if (this.tickOptions == null || !this.tickOptions.formatString) { + this._overrideFormatString = true; + } + + this.min = rmin; + this.max = rmax; + var range = this.max - this.min; + + var minorTicks = (this.minorTicks === 'auto') ? 0 : this.minorTicks; + var numberTicks; + if (this.numberTicks == null){ + if (dim > 140) { + numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1); + if (numberTicks < 2) { + numberTicks = 2; + } + if (minorTicks === 0) { + var temp = dim/(numberTicks - 1); + if (temp < 100) { + minorTicks = 0; + } + else if (temp < 190) { + minorTicks = 1; + } + else if (temp < 250) { + minorTicks = 3; + } + else if (temp < 600) { + minorTicks = 4; + } + else { + minorTicks = 9; + } + } + } + else { + numberTicks = 2; + if (minorTicks === 0) { + minorTicks = 1; + } + minorTicks = 0; + } + } + else { + numberTicks = this.numberTicks; + } + + if (order >= 0 && minorTicks !== 3) { + this._autoFormatString = '%d'; + } + // Adjust format string for case with 3 ticks where we'll have like 1, 2.5, 5, 7.5, 10 + else if (order <= 0 && minorTicks === 3) { + var temp = -(order - 1); + this._autoFormatString = '%.'+ Math.abs(order-1) + 'f'; + } + + // Adjust format string for values less than 1. + else if (order < 0) { + var temp = -order; + this._autoFormatString = '%.'+ Math.abs(order) + 'f'; + } + + else { + this._autoFormatString = '%d'; + } + + var to, t, val, tt1, spread, interval; + for (var i=0; i<numberTicks; i++){ + tt = Math.pow(this.base, i - numberTicks + 1) * this.max; + + t = new this.tickRenderer(this.tickOptions); + + if (this._overrideFormatString) { + t.formatString = this._autoFormatString; + } + + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(tt, this.name); + this._ticks.push(t); + + if (minorTicks && i<numberTicks-1) { + tt1 = Math.pow(this.base, i - numberTicks + 2) * this.max; + spread = tt1 - tt; + interval = tt1 / (minorTicks+1); + for (var j=minorTicks-1; j>=0; j--) { + val = tt1-interval*(j+1); + t = new this.tickRenderer(this.tickOptions); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(val, this.name); + this._ticks.push(t); + } + } + } + } + + // min and max are set as would be the case with zooming + else if (this.min != null && this.max != null) { + var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); + var nt, ti; + // don't have an interval yet, pick one that gives the most + // "round" ticks we can get. + if (this.numberTicks == null && this.tickInterval == null) { + // var threshold = 30; + var tdim = Math.max(dim, threshold+1); + var nttarget = Math.ceil((tdim-threshold)/35 + 1); + + var ret = $.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min, this.max, nttarget); + + this._autoFormatString = ret[3]; + nt = ret[2]; + ti = ret[4]; + + for (var i=0; i<nt; i++) { + opts.value = this.min + i * ti; + t = new this.tickRenderer(opts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + this._ticks.push(t); + } + } + + // for loose zoom, number ticks and interval are also set. + else if (this.numberTicks != null && this.tickInterval != null) { + nt = this.numberTicks; + for (var i=0; i<nt; i++) { + opts.value = this.min + i * this.tickInterval; + t = new this.tickRenderer(opts); + + if (this._overrideFormatString && this._autoFormatString != '') { + t.formatString = this._autoFormatString; + } + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + this._ticks.push(t); + } + } + } + }; + + $.jqplot.LogAxisRenderer.prototype.pack = function(pos, offsets) { + var lb = parseInt(this.base, 10); + var ticks = this._ticks; + var trans = function (v) { return Math.log(v)/Math.log(lb); }; + var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); }; + var max = trans(this.max); + var min = trans(this.min); + var offmax = offsets.max; + var offmin = offsets.min; + var lshow = (this._label == null) ? false : this._label.show; + + for (var p in pos) { + this._elem.css(p, pos[p]); + } + + this._offsets = offsets; + // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. + var pixellength = offmax - offmin; + var unitlength = max - min; + + // point to unit and unit to point conversions references to Plot DOM element top left corner. + this.p2u = function(p){ + return invtrans((p - offmin) * unitlength / pixellength + min); + }; + + this.u2p = function(u){ + return (trans(u) - min) * pixellength / unitlength + offmin; + }; + + if (this.name == 'xaxis' || this.name == 'x2axis'){ + this.series_u2p = function(u){ + return (trans(u) - min) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return invtrans(p * unitlength / pixellength + min); + }; + } + // yaxis is max at top of canvas. + else { + this.series_u2p = function(u){ + return (trans(u) - max) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return invtrans(p * unitlength / pixellength + max); + }; + } + + if (this.show) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + switch (t.labelPosition) { + case 'auto': + // position at end + if (t.angle < 0) { + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + } + // position at start + else { + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + } + break; + case 'end': + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + case 'start': + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + break; + case 'middle': + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + default: + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + } + } + else { + shim = -t.getWidth()/2; + } + // var shim = t.getWidth()/2; + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('left', val); + t.pack(); + } + } + if (lshow) { + var w = this._label._elem.outerWidth(true); + this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); + if (this.name == 'xaxis') { + this._label._elem.css('bottom', '0px'); + } + else { + this._label._elem.css('top', '0px'); + } + this._label.pack(); + } + } + else { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + switch (t.labelPosition) { + case 'auto': + // position at end + case 'end': + if (t.angle < 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'start': + if (t.angle > 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'middle': + // if (t.angle > 0) { + // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + // } + // else { + // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + // } + shim = -t.getHeight()/2; + break; + default: + shim = -t.getHeight()/2; + break; + } + } + else { + shim = -t.getHeight()/2; + } + + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('top', val); + t.pack(); + } + } + if (lshow) { + var h = this._label._elem.outerHeight(true); + this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); + if (this.name == 'yaxis') { + this._label._elem.css('left', '0px'); + } + else { + this._label._elem.css('right', '0px'); + } + this._label.pack(); + } + } + } + }; +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.min.js new file mode 100644 index 0000000..855f9a1 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.logAxisRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.LogAxisRenderer=function(){a.jqplot.LinearAxisRenderer.call(this);this.axisDefaults={base:10,tickDistribution:"power"}};a.jqplot.LogAxisRenderer.prototype=new a.jqplot.LinearAxisRenderer();a.jqplot.LogAxisRenderer.prototype.constructor=a.jqplot.LogAxisRenderer;a.jqplot.LogAxisRenderer.prototype.init=function(b){this.drawBaseline=true;this.minorTicks="auto";this._scalefact=1;a.extend(true,this,b);this._autoFormatString="%d";this._overrideFormatString=false;for(var c in this.renderer.axisDefaults){if(this[c]==null){this[c]=this.renderer.axisDefaults[c]}}this.resetDataBounds()};a.jqplot.LogAxisRenderer.prototype.createTicks=function(d){var G=this._ticks;var w=this.ticks;var s=this.name;var u=this._dataBounds;var b=(this.name.charAt(0)==="x")?this._plotDimensions.width:this._plotDimensions.height;var k;var N,v;var m,l;var M,K;var g=30;this._scalefact=(Math.max(b,g+1)-g)/300;if(w.length){for(K=0;K<w.length;K++){var A=w[K];var H=new this.tickRenderer(this.tickOptions);if(A.constructor==Array){H.value=A[0];H.label=A[1];if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}H.setTick(A[0],this.name);this._ticks.push(H)}else{if(a.isPlainObject(A)){a.extend(true,H,A);H.axis=this.name;this._ticks.push(H)}else{H.value=A;if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}H.setTick(A,this.name);this._ticks.push(H)}}}this.numberTicks=w.length;this.min=this._ticks[0].value;this.max=this._ticks[this.numberTicks-1].value}else{if(this.min==null&&this.max==null){N=u.min*(2-this.padMin);v=u.max*this.padMax;if(N==v){var c=0.05;N=N*(1-c);v=v*(1+c)}if(this.min!=null&&this.min<=0){throw new Error("Log axis minimum must be greater than 0")}if(this.max!=null&&this.max<=0){throw new Error("Log axis maximum must be greater than 0")}function f(j){var i=Math.pow(10,Math.floor(Math.log(j)/Math.LN10));return Math.ceil(j/i)*i}function x(j){var i=Math.pow(10,Math.floor(Math.log(j)/Math.LN10));return Math.floor(j/i)*i}var F,r;F=Math.pow(this.base,Math.floor(Math.log(N)/Math.log(this.base)));r=Math.pow(this.base,Math.ceil(Math.log(v)/Math.log(this.base)));if(F===0){F=1}var E=Math.round(Math.log(F)/Math.LN10);if(this.tickOptions==null||!this.tickOptions.formatString){this._overrideFormatString=true}this.min=F;this.max=r;var q=this.max-this.min;var C=(this.minorTicks==="auto")?0:this.minorTicks;var h;if(this.numberTicks==null){if(b>140){h=Math.round(Math.log(this.max/this.min)/Math.log(this.base)+1);if(h<2){h=2}if(C===0){var o=b/(h-1);if(o<100){C=0}else{if(o<190){C=1}else{if(o<250){C=3}else{if(o<600){C=4}else{C=9}}}}}}else{h=2;if(C===0){C=1}C=0}}else{h=this.numberTicks}if(E>=0&&C!==3){this._autoFormatString="%d"}else{if(E<=0&&C===3){var o=-(E-1);this._autoFormatString="%."+Math.abs(E-1)+"f"}else{if(E<0){var o=-E;this._autoFormatString="%."+Math.abs(E)+"f"}else{this._autoFormatString="%d"}}}var O,H,z,p,n,k;for(var K=0;K<h;K++){M=Math.pow(this.base,K-h+1)*this.max;H=new this.tickRenderer(this.tickOptions);if(this._overrideFormatString){H.formatString=this._autoFormatString}if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}H.setTick(M,this.name);this._ticks.push(H);if(C&&K<h-1){p=Math.pow(this.base,K-h+2)*this.max;n=p-M;k=p/(C+1);for(var J=C-1;J>=0;J--){z=p-k*(J+1);H=new this.tickRenderer(this.tickOptions);if(this._overrideFormatString&&this._autoFormatString!=""){H.formatString=this._autoFormatString}if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}H.setTick(z,this.name);this._ticks.push(H)}}}}else{if(this.min!=null&&this.max!=null){var y=a.extend(true,{},this.tickOptions,{name:this.name,value:null});var I,e;if(this.numberTicks==null&&this.tickInterval==null){var D=Math.max(b,g+1);var L=Math.ceil((D-g)/35+1);var B=a.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min,this.max,L);this._autoFormatString=B[3];I=B[2];e=B[4];for(var K=0;K<I;K++){y.value=this.min+K*e;H=new this.tickRenderer(y);if(this._overrideFormatString&&this._autoFormatString!=""){H.formatString=this._autoFormatString}if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}this._ticks.push(H)}}else{if(this.numberTicks!=null&&this.tickInterval!=null){I=this.numberTicks;for(var K=0;K<I;K++){y.value=this.min+K*this.tickInterval;H=new this.tickRenderer(y);if(this._overrideFormatString&&this._autoFormatString!=""){H.formatString=this._autoFormatString}if(!this.showTicks){H.showLabel=false;H.showMark=false}else{if(!this.showTickMarks){H.showMark=false}}this._ticks.push(H)}}}}}}};a.jqplot.LogAxisRenderer.prototype.pack=function(f,e){var r=parseInt(this.base,10);var y=this._ticks;var d=function(h){return Math.log(h)/Math.log(r)};var b=function(h){return Math.pow(Math.E,(Math.log(r)*h))};var u=d(this.max);var s=d(this.min);var m=e.max;var k=e.min;var o=(this._label==null)?false:this._label.show;for(var q in f){this._elem.css(q,f[q])}this._offsets=e;var g=m-k;var j=u-s;this.p2u=function(h){return b((h-k)*j/g+s)};this.u2p=function(h){return(d(h)-s)*g/j+k};if(this.name=="xaxis"||this.name=="x2axis"){this.series_u2p=function(h){return(d(h)-s)*g/j};this.series_p2u=function(h){return b(h*j/g+s)}}else{this.series_u2p=function(h){return(d(h)-u)*g/j};this.series_p2u=function(h){return b(h*j/g+u)}}if(this.show){if(this.name=="xaxis"||this.name=="x2axis"){for(var v=0;v<y.length;v++){var n=y[v];if(n.show&&n.showLabel){var c;if(n.constructor==a.jqplot.CanvasAxisTickRenderer&&n.angle){switch(n.labelPosition){case"auto":if(n.angle<0){c=-n.getWidth()+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2}else{c=-n._textRenderer.height*Math.sin(n._textRenderer.angle)/2}break;case"end":c=-n.getWidth()+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break;case"start":c=-n._textRenderer.height*Math.sin(n._textRenderer.angle)/2;break;case"middle":c=-n.getWidth()/2+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break;default:c=-n.getWidth()/2+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break}}else{c=-n.getWidth()/2}var z=this.u2p(n.value)+c+"px";n._elem.css("left",z);n.pack()}}if(o){var l=this._label._elem.outerWidth(true);this._label._elem.css("left",k+g/2-l/2+"px");if(this.name=="xaxis"){this._label._elem.css("bottom","0px")}else{this._label._elem.css("top","0px")}this._label.pack()}}else{for(var v=0;v<y.length;v++){var n=y[v];if(n.show&&n.showLabel){var c;if(n.constructor==a.jqplot.CanvasAxisTickRenderer&&n.angle){switch(n.labelPosition){case"auto":case"end":if(n.angle<0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"start":if(n.angle>0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"middle":c=-n.getHeight()/2;break;default:c=-n.getHeight()/2;break}}else{c=-n.getHeight()/2}var z=this.u2p(n.value)+c+"px";n._elem.css("top",z);n.pack()}}if(o){var x=this._label._elem.outerHeight(true);this._label._elem.css("top",m-g/2-x/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{this._label._elem.css("right","0px")}this._label.pack()}}}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.js b/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.js new file mode 100644 index 0000000..b90c5bc --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.js @@ -0,0 +1,611 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + // class: $.jqplot.MekkoAxisRenderer + // An axis renderer for a Mekko chart. + // Should be used with a Mekko chart where the mekkoRenderer is used on the series. + // Displays the Y axis as a range from 0 to 1 (0 to 100%) and the x axis with a tick + // for each series scaled to the sum of all the y values. + $.jqplot.MekkoAxisRenderer = function() { + }; + + // called with scope of axis object. + $.jqplot.MekkoAxisRenderer.prototype.init = function(options){ + // prop: tickMode + // How to space the ticks on the axis. + // 'bar' will place a tick at the width of each bar. + // This is the default for the x axis. + // 'even' will place ticks at even intervals. This is + // the default for x2 axis and y axis. y axis cannot be changed. + this.tickMode; + // prop: barLabelRenderer + // renderer to use to draw labels under each bar. + this.barLabelRenderer = $.jqplot.AxisLabelRenderer; + // prop: barLabels + // array of labels to put under each bar. + this.barLabels = this.barLabels || []; + // prop: barLabelOptions + // options object to pass to the bar label renderer. + this.barLabelOptions = {}; + this.tickOptions = $.extend(true, {showGridline:false}, this.tickOptions); + this._barLabels = []; + $.extend(true, this, options); + if (this.name == 'yaxis') { + this.tickOptions.formatString = this.tickOptions.formatString || "%d\%"; + } + var db = this._dataBounds; + db.min = 0; + // for y axes, scale always go from 0 to 1 (0 to 100%) + if (this.name == 'yaxis' || this.name == 'y2axis') { + db.max = 100; + this.tickMode = 'even'; + } + // For x axes, scale goes from 0 to sum of all y values. + else if (this.name == 'xaxis'){ + this.tickMode = (this.tickMode == null) ? 'bar' : this.tickMode; + for (var i=0; i<this._series.length; i++) { + db.max += this._series[i]._sumy; + } + } + else if (this.name == 'x2axis'){ + this.tickMode = (this.tickMode == null) ? 'even' : this.tickMode; + for (var i=0; i<this._series.length; i++) { + db.max += this._series[i]._sumy; + } + } + }; + + // called with scope of axis + $.jqplot.MekkoAxisRenderer.prototype.draw = function(ctx, plot) { + if (this.show) { + // populate the axis label and value properties. + // createTicks is a method on the renderer, but + // call it within the scope of the axis. + this.renderer.createTicks.call(this); + // fill a div with axes labels in the right direction. + // Need to pregenerate each axis to get its bounds and + // position it and the labels correctly on the plot. + var dim=0; + var temp; + + var elem = document.createElement('div'); + this._elem = $(elem); + this._elem.addClass('jqplot-axis jqplot-'+this.name); + this._elem.css('position', 'absolute'); + elem = null; + + if (this.name == 'xaxis' || this.name == 'x2axis') { + this._elem.width(this._plotDimensions.width); + } + else { + this._elem.height(this._plotDimensions.height); + } + + // draw the axis label + // create a _label object. + this.labelOptions.axis = this.name; + this._label = new this.labelRenderer(this.labelOptions); + if (this._label.show) { + this._elem.append(this._label.draw(ctx)); + } + + var t, tick, elem; + if (this.showTicks) { + t = this._ticks; + for (var i=0; i<t.length; i++) { + tick = t[i]; + if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + this._elem.append(tick.draw(ctx)); + } + } + } + + // draw the series labels + for (i=0; i<this.barLabels.length; i++) { + this.barLabelOptions.axis = this.name; + this.barLabelOptions.label = this.barLabels[i]; + this._barLabels.push(new this.barLabelRenderer(this.barLabelOptions)); + if (this.tickMode != 'bar') { + this._barLabels[i].show = false; + } + if (this._barLabels[i].show) { + var elem = this._barLabels[i].draw(ctx, plot); + elem.removeClass('jqplot-'+this.name+'-label'); + elem.addClass('jqplot-'+this.name+'-tick'); + elem.addClass('jqplot-mekko-barLabel'); + elem.appendTo(this._elem); + elem = null; + } + } + + } + return this._elem; + }; + + // called with scope of an axis + $.jqplot.MekkoAxisRenderer.prototype.reset = function() { + this.min = this._min; + this.max = this._max; + this.tickInterval = this._tickInterval; + this.numberTicks = this._numberTicks; + // this._ticks = this.__ticks; + }; + + // called with scope of axis + $.jqplot.MekkoAxisRenderer.prototype.set = function() { + var dim = 0; + var temp; + var w = 0; + var h = 0; + var lshow = (this._label == null) ? false : this._label.show; + if (this.show && this.showTicks) { + var t = this._ticks; + for (var i=0; i<t.length; i++) { + var tick = t[i]; + if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + temp = tick._elem.outerHeight(true); + } + else { + temp = tick._elem.outerWidth(true); + } + if (temp > dim) { + dim = temp; + } + } + } + + if (lshow) { + w = this._label._elem.outerWidth(true); + h = this._label._elem.outerHeight(true); + } + if (this.name == 'xaxis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); + } + else if (this.name == 'x2axis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); + } + else if (this.name == 'yaxis') { + dim = dim + w; + this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + else { + dim = dim + w; + this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + } + }; + + // called with scope of axis + $.jqplot.MekkoAxisRenderer.prototype.createTicks = function() { + // we're are operating on an axis here + var ticks = this._ticks; + var userTicks = this.ticks; + var name = this.name; + // databounds were set on axis initialization. + var db = this._dataBounds; + var dim, interval; + var min, max; + var pos1, pos2; + var t, tt, i, j; + + // if we already have ticks, use them. + // ticks must be in order of increasing value. + + if (userTicks.length) { + // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed + for (i=0; i<userTicks.length; i++){ + var ut = userTicks[i]; + var t = new this.tickRenderer(this.tickOptions); + if (ut.constructor == Array) { + t.value = ut[0]; + t.label = ut[1]; + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(ut[0], this.name); + this._ticks.push(t); + } + + else { + t.value = ut; + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(ut, this.name); + this._ticks.push(t); + } + } + this.numberTicks = userTicks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this.numberTicks-1].value; + this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); + } + + // we don't have any ticks yet, let's make some! + else { + if (name == 'xaxis' || name == 'x2axis') { + dim = this._plotDimensions.width; + } + else { + dim = this._plotDimensions.height; + } + + // if min, max and number of ticks specified, user can't specify interval. + if (this.min != null && this.max != null && this.numberTicks != null) { + this.tickInterval = null; + } + + min = (this.min != null) ? this.min : db.min; + max = (this.max != null) ? this.max : db.max; + + // if min and max are same, space them out a bit.+ + if (min == max) { + var adj = 0.05; + if (min > 0) { + adj = Math.max(Math.log(min)/Math.LN10, 0.05); + } + min -= adj; + max += adj; + } + + var range = max - min; + var rmin, rmax; + var temp, prev, curr; + var ynumticks = [3,5,6,11,21]; + + // yaxis divide ticks in nice intervals from 0 to 1. + if (this.name == 'yaxis' || this.name == 'y2axis') { + this.min = 0; + this.max = 100; + // user didn't specify number of ticks. + if (!this.numberTicks){ + if (this.tickInterval) { + this.numberTicks = 3 + Math.ceil(range / this.tickInterval); + } + else { + temp = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); + for (i=0; i<ynumticks.length; i++) { + curr = temp/ynumticks[i]; + if (curr == 1) { + this.numberTicks = ynumticks[i]; + break; + } + else if (curr > 1) { + prev = curr; + continue; + } + else if (curr < 1) { + // was prev or is curr closer to one? + if (Math.abs(prev - 1) < Math.abs(curr - 1)) { + this.numberTicks = ynumticks[i-1]; + break; + } + else { + this.numberTicks = ynumticks[i]; + break; + } + } + else if (i == ynumticks.length -1) { + this.numberTicks = ynumticks[i]; + } + } + this.tickInterval = range / (this.numberTicks - 1); + } + } + + // user did specify number of ticks. + else { + this.tickInterval = range / (this.numberTicks - 1); + } + + for (var i=0; i<this.numberTicks; i++){ + tt = this.min + i * this.tickInterval; + t = new this.tickRenderer(this.tickOptions); + // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(tt, this.name); + this._ticks.push(t); + } + } + + // for x axes, have number ot ticks equal to number of series and ticks placed + // at sum of y values for each series. + else if (this.tickMode == 'bar') { + this.min = 0; + this.numberTicks = this._series.length + 1; + t = new this.tickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(0, this.name); + this._ticks.push(t); + + temp = 0; + + for (i=1; i<this.numberTicks; i++){ + temp += this._series[i-1]._sumy; + t = new this.tickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(temp, this.name); + this._ticks.push(t); + } + this.max = this.max || temp; + + // if user specified a max and it is greater than sum, add a tick + if (this.max > temp) { + t = new this.tickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(this.max, this.name); + this._ticks.push(t); + + } + } + + else if (this.tickMode == 'even') { + this.min = 0; + this.max = this.max || db.max; + // get a desired number of ticks + var nt = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); + range = this.max - this.min; + this.numberTicks = nt; + this.tickInterval = range / (this.numberTicks - 1); + + for (i=0; i<this.numberTicks; i++){ + tt = this.min + i * this.tickInterval; + t = new this.tickRenderer(this.tickOptions); + // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); + if (!this.showTicks) { + t.showLabel = false; + t.showMark = false; + } + else if (!this.showTickMarks) { + t.showMark = false; + } + t.setTick(tt, this.name); + this._ticks.push(t); + } + + } + } + }; + + // called with scope of axis + $.jqplot.MekkoAxisRenderer.prototype.pack = function(pos, offsets) { + var ticks = this._ticks; + var max = this.max; + var min = this.min; + var offmax = offsets.max; + var offmin = offsets.min; + var lshow = (this._label == null) ? false : this._label.show; + + for (var p in pos) { + this._elem.css(p, pos[p]); + } + + this._offsets = offsets; + // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. + var pixellength = offmax - offmin; + var unitlength = max - min; + + // point to unit and unit to point conversions references to Plot DOM element top left corner. + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + this.u2p = function(u){ + return (u - min) * pixellength / unitlength + offmin; + }; + + if (this.name == 'xaxis' || this.name == 'x2axis'){ + this.series_u2p = function(u){ + return (u - min) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + else { + this.series_u2p = function(u){ + return (u - max) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + + if (this.show) { + if (this.name == 'xaxis' || this.name == 'x2axis') { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'xaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + if (temp * t.angle < 0) { + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + } + // position at start + else { + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + } + break; + case 'end': + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + case 'start': + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + break; + case 'middle': + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + default: + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + } + } + else { + shim = -t.getWidth()/2; + } + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('left', val); + t.pack(); + } + } + var w; + if (lshow) { + w = this._label._elem.outerWidth(true); + this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); + if (this.name == 'xaxis') { + this._label._elem.css('bottom', '0px'); + } + else { + this._label._elem.css('top', '0px'); + } + this._label.pack(); + } + // now show the labels under the bars. + var b, l, r; + for (var i=0; i<this.barLabels.length; i++) { + b = this._barLabels[i]; + if (b.show) { + w = b.getWidth(); + l = this._ticks[i].getLeft() + this._ticks[i].getWidth(); + r = this._ticks[i+1].getLeft(); + b._elem.css('left', (r+l-w)/2+'px'); + b._elem.css('top', this._ticks[i]._elem.css('top')); + b.pack(); + } + } + } + else { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'yaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + case 'end': + if (temp * t.angle < 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'start': + if (t.angle > 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'middle': + shim = -t.getHeight()/2; + break; + default: + shim = -t.getHeight()/2; + break; + } + } + else { + shim = -t.getHeight()/2; + } + + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('top', val); + t.pack(); + } + } + if (lshow) { + var h = this._label._elem.outerHeight(true); + this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); + if (this.name == 'yaxis') { + this._label._elem.css('left', '0px'); + } + else { + this._label._elem.css('right', '0px'); + } + this._label.pack(); + } + } + } + }; +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.min.js new file mode 100644 index 0000000..7969de7 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mekkoAxisRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.MekkoAxisRenderer=function(){};a.jqplot.MekkoAxisRenderer.prototype.init=function(c){this.tickMode;this.barLabelRenderer=a.jqplot.AxisLabelRenderer;this.barLabels=this.barLabels||[];this.barLabelOptions={};this.tickOptions=a.extend(true,{showGridline:false},this.tickOptions);this._barLabels=[];a.extend(true,this,c);if(this.name=="yaxis"){this.tickOptions.formatString=this.tickOptions.formatString||"%d%"}var b=this._dataBounds;b.min=0;if(this.name=="yaxis"||this.name=="y2axis"){b.max=100;this.tickMode="even"}else{if(this.name=="xaxis"){this.tickMode=(this.tickMode==null)?"bar":this.tickMode;for(var d=0;d<this._series.length;d++){b.max+=this._series[d]._sumy}}else{if(this.name=="x2axis"){this.tickMode=(this.tickMode==null)?"even":this.tickMode;for(var d=0;d<this._series.length;d++){b.max+=this._series[d]._sumy}}}}};a.jqplot.MekkoAxisRenderer.prototype.draw=function(b,j){if(this.show){this.renderer.createTicks.call(this);var h=0;var c;var g=document.createElement("div");this._elem=a(g);this._elem.addClass("jqplot-axis jqplot-"+this.name);this._elem.css("position","absolute");g=null;if(this.name=="xaxis"||this.name=="x2axis"){this._elem.width(this._plotDimensions.width)}else{this._elem.height(this._plotDimensions.height)}this.labelOptions.axis=this.name;this._label=new this.labelRenderer(this.labelOptions);if(this._label.show){this._elem.append(this._label.draw(b))}var f,e,g;if(this.showTicks){f=this._ticks;for(var d=0;d<f.length;d++){e=f[d];if(e.showLabel&&(!e.isMinorTick||this.showMinorTicks)){this._elem.append(e.draw(b))}}}for(d=0;d<this.barLabels.length;d++){this.barLabelOptions.axis=this.name;this.barLabelOptions.label=this.barLabels[d];this._barLabels.push(new this.barLabelRenderer(this.barLabelOptions));if(this.tickMode!="bar"){this._barLabels[d].show=false}if(this._barLabels[d].show){var g=this._barLabels[d].draw(b,j);g.removeClass("jqplot-"+this.name+"-label");g.addClass("jqplot-"+this.name+"-tick");g.addClass("jqplot-mekko-barLabel");g.appendTo(this._elem);g=null}}}return this._elem};a.jqplot.MekkoAxisRenderer.prototype.reset=function(){this.min=this._min;this.max=this._max;this.tickInterval=this._tickInterval;this.numberTicks=this._numberTicks};a.jqplot.MekkoAxisRenderer.prototype.set=function(){var k=0;var d;var c=0;var j=0;var b=(this._label==null)?false:this._label.show;if(this.show&&this.showTicks){var g=this._ticks;for(var f=0;f<g.length;f++){var e=g[f];if(e.showLabel&&(!e.isMinorTick||this.showMinorTicks)){if(this.name=="xaxis"||this.name=="x2axis"){d=e._elem.outerHeight(true)}else{d=e._elem.outerWidth(true)}if(d>k){k=d}}}if(b){c=this._label._elem.outerWidth(true);j=this._label._elem.outerHeight(true)}if(this.name=="xaxis"){k=k+j;this._elem.css({height:k+"px",left:"0px",bottom:"0px"})}else{if(this.name=="x2axis"){k=k+j;this._elem.css({height:k+"px",left:"0px",top:"0px"})}else{if(this.name=="yaxis"){k=k+c;this._elem.css({width:k+"px",left:"0px",top:"0px"});if(b&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",c+"px")}}else{k=k+c;this._elem.css({width:k+"px",right:"0px",top:"0px"});if(b&&this._label.constructor==a.jqplot.AxisLabelRenderer){this._label._elem.css("width",c+"px")}}}}}};a.jqplot.MekkoAxisRenderer.prototype.createTicks=function(){var z=this._ticks;var w=this.ticks;var B=this.name;var y=this._dataBounds;var p,x;var n,r;var d,c;var h,b,s,q;if(w.length){for(s=0;s<w.length;s++){var e=w[s];var h=new this.tickRenderer(this.tickOptions);if(e.constructor==Array){h.value=e[0];h.label=e[1];if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(e[0],this.name);this._ticks.push(h)}else{h.value=e;if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(e,this.name);this._ticks.push(h)}}this.numberTicks=w.length;this.min=this._ticks[0].value;this.max=this._ticks[this.numberTicks-1].value;this.tickInterval=(this.max-this.min)/(this.numberTicks-1)}else{if(B=="xaxis"||B=="x2axis"){p=this._plotDimensions.width}else{p=this._plotDimensions.height}if(this.min!=null&&this.max!=null&&this.numberTicks!=null){this.tickInterval=null}n=(this.min!=null)?this.min:y.min;r=(this.max!=null)?this.max:y.max;if(n==r){var g=0.05;if(n>0){g=Math.max(Math.log(n)/Math.LN10,0.05)}n-=g;r+=g}var k=r-n;var m,o;var v,l,u;var f=[3,5,6,11,21];if(this.name=="yaxis"||this.name=="y2axis"){this.min=0;this.max=100;if(!this.numberTicks){if(this.tickInterval){this.numberTicks=3+Math.ceil(k/this.tickInterval)}else{v=2+Math.ceil((p-(this.tickSpacing-1))/this.tickSpacing);for(s=0;s<f.length;s++){u=v/f[s];if(u==1){this.numberTicks=f[s];break}else{if(u>1){l=u;continue}else{if(u<1){if(Math.abs(l-1)<Math.abs(u-1)){this.numberTicks=f[s-1];break}else{this.numberTicks=f[s];break}}else{if(s==f.length-1){this.numberTicks=f[s]}}}}}this.tickInterval=k/(this.numberTicks-1)}}else{this.tickInterval=k/(this.numberTicks-1)}for(var s=0;s<this.numberTicks;s++){b=this.min+s*this.tickInterval;h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(b,this.name);this._ticks.push(h)}}else{if(this.tickMode=="bar"){this.min=0;this.numberTicks=this._series.length+1;h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(0,this.name);this._ticks.push(h);v=0;for(s=1;s<this.numberTicks;s++){v+=this._series[s-1]._sumy;h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(v,this.name);this._ticks.push(h)}this.max=this.max||v;if(this.max>v){h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(this.max,this.name);this._ticks.push(h)}}else{if(this.tickMode=="even"){this.min=0;this.max=this.max||y.max;var A=2+Math.ceil((p-(this.tickSpacing-1))/this.tickSpacing);k=this.max-this.min;this.numberTicks=A;this.tickInterval=k/(this.numberTicks-1);for(s=0;s<this.numberTicks;s++){b=this.min+s*this.tickInterval;h=new this.tickRenderer(this.tickOptions);if(!this.showTicks){h.showLabel=false;h.showMark=false}else{if(!this.showTickMarks){h.showMark=false}}h.setTick(b,this.name);this._ticks.push(h)}}}}}};a.jqplot.MekkoAxisRenderer.prototype.pack=function(e,d){var C=this._ticks;var x=this.max;var v=this.min;var m=d.max;var j=d.min;var o=(this._label==null)?false:this._label.show;for(var s in e){this._elem.css(s,e[s])}this._offsets=d;var f=m-j;var g=x-v;this.p2u=function(b){return(b-j)*g/f+v};this.u2p=function(b){return(b-v)*f/g+j};if(this.name=="xaxis"||this.name=="x2axis"){this.series_u2p=function(b){return(b-v)*f/g};this.series_p2u=function(b){return b*g/f+v}}else{this.series_u2p=function(b){return(b-x)*f/g};this.series_p2u=function(b){return b*g/f+x}}if(this.show){if(this.name=="xaxis"||this.name=="x2axis"){for(var y=0;y<C.length;y++){var n=C[y];if(n.show&&n.showLabel){var c;if(n.constructor==a.jqplot.CanvasAxisTickRenderer&&n.angle){var A=(this.name=="xaxis")?1:-1;switch(n.labelPosition){case"auto":if(A*n.angle<0){c=-n.getWidth()+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2}else{c=-n._textRenderer.height*Math.sin(n._textRenderer.angle)/2}break;case"end":c=-n.getWidth()+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break;case"start":c=-n._textRenderer.height*Math.sin(n._textRenderer.angle)/2;break;case"middle":c=-n.getWidth()/2+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break;default:c=-n.getWidth()/2+n._textRenderer.height*Math.sin(-n._textRenderer.angle)/2;break}}else{c=-n.getWidth()/2}var D=this.u2p(n.value)+c+"px";n._elem.css("left",D);n.pack()}}var k;if(o){k=this._label._elem.outerWidth(true);this._label._elem.css("left",j+f/2-k/2+"px");if(this.name=="xaxis"){this._label._elem.css("bottom","0px")}else{this._label._elem.css("top","0px")}this._label.pack()}var B,u,q;for(var y=0;y<this.barLabels.length;y++){B=this._barLabels[y];if(B.show){k=B.getWidth();u=this._ticks[y].getLeft()+this._ticks[y].getWidth();q=this._ticks[y+1].getLeft();B._elem.css("left",(q+u-k)/2+"px");B._elem.css("top",this._ticks[y]._elem.css("top"));B.pack()}}}else{for(var y=0;y<C.length;y++){var n=C[y];if(n.show&&n.showLabel){var c;if(n.constructor==a.jqplot.CanvasAxisTickRenderer&&n.angle){var A=(this.name=="yaxis")?1:-1;switch(n.labelPosition){case"auto":case"end":if(A*n.angle<0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"start":if(n.angle>0){c=-n._textRenderer.height*Math.cos(-n._textRenderer.angle)/2}else{c=-n.getHeight()+n._textRenderer.height*Math.cos(n._textRenderer.angle)/2}break;case"middle":c=-n.getHeight()/2;break;default:c=-n.getHeight()/2;break}}else{c=-n.getHeight()/2}var D=this.u2p(n.value)+c+"px";n._elem.css("top",D);n.pack()}}if(o){var z=this._label._elem.outerHeight(true);this._label._elem.css("top",m-f/2-z/2+"px");if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{this._label._elem.css("right","0px")}this._label.pack()}}}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.js b/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.js new file mode 100644 index 0000000..05f495f --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.js @@ -0,0 +1,437 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.MekkoRenderer + * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph. + * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts. The mekko renderer + * overrides the default legend renderer with its own $.jqplot.MekkoLegendRenderer + * which allows more flexibility to specify number of rows and columns in the legend. + * + * Data is specified per bar in the chart. You can specify data as an array of y values, or as + * an array of [label, value] pairs. Note that labels are used only on the first series. + * Labels on subsequent series are ignored: + * + * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]]; + * > bar2 = [15,6,9,13,6]; + * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]]; + * + * If you want to place labels for each bar under the axis, you use the barLabels option on + * the axes. The bar labels can be styled with the ".jqplot-mekko-barLabel" css class. + * + * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy']; + * > axes:{xaxis:{barLabels:barLabels}} + * + */ + + + $.jqplot.MekkoRenderer = function(){ + this.shapeRenderer = new $.jqplot.ShapeRenderer(); + // prop: borderColor + // color of the borders between areas on the chart + this.borderColor = null; + // prop: showBorders + // True to draw borders lines between areas on the chart. + // False will draw borders lines with the same color as the area. + this.showBorders = true; + }; + + // called with scope of series. + $.jqplot.MekkoRenderer.prototype.init = function(options, plot) { + this.fill = false; + this.fillRect = true; + this.strokeRect = true; + this.shadow = false; + // width of bar on x axis. + this._xwidth = 0; + this._xstart = 0; + $.extend(true, this.renderer, options); + // set the shape renderer options + var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect}; + this.renderer.shapeRenderer.init(opts); + plot.axes.x2axis._series.push(this); + this._type = 'mekko'; + }; + + // Method: setGridData + // converts the user data values to grid coordinates and stores them + // in the gridData array. Will convert user data into appropriate + // rectangles. + // Called with scope of a series. + $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var data = this._plotData; + this.gridData = []; + // figure out width on x axis. + // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth(); + this._xwidth = xp(this._sumy) - xp(0); + if (this.index>0) { + this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth; + } + var totheight = this.canvas.getHeight(); + var sumy = 0; + var cury; + var curheight; + for (var i=0; i<data.length; i++) { + if (data[i] != null) { + sumy += data[i][1]; + cury = totheight - (sumy / this._sumy * totheight); + curheight = data[i][1] / this._sumy * totheight; + this.gridData.push([this._xstart, cury, this._xwidth, curheight]); + } + } + }; + + // Method: makeGridData + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + // figure out width on x axis. + var xp = this._xaxis.series_u2p; + var totheight = this.canvas.getHeight(); + var sumy = 0; + var cury; + var curheight; + var gd = []; + for (var i=0; i<data.length; i++) { + if (data[i] != null) { + sumy += data[i][1]; + cury = totheight - (sumy / this._sumy * totheight); + curheight = data[i][1] / this._sumy * totheight; + gd.push([this._xstart, cury, this._xwidth, curheight]); + } + } + return gd; + }; + + + // called within scope of series. + $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) { + var i; + var opts = (options != undefined) ? options : {}; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); + ctx.save(); + if (gd.length) { + if (showLine) { + for (i=0; i<gd.length; i++){ + opts.fillStyle = colorGenerator.next(); + if (this.renderer.showBorders) { + opts.strokeStyle = this.renderer.borderColor; + } + else { + opts.strokeStyle = opts.fillStyle; + } + this.renderer.shapeRenderer.draw(ctx, gd[i], opts); + } + } + } + + ctx.restore(); + }; + + $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) { + // This is a no-op, no shadows on mekko charts. + }; + + /** + * Class: $.jqplot.MekkoLegendRenderer + * Legend renderer used by mekko charts with options for + * controlling number or rows and columns as well as placement + * outside of plot area. + * + */ + $.jqplot.MekkoLegendRenderer = function(){ + // + }; + + $.jqplot.MekkoLegendRenderer.prototype.init = function(options) { + // prop: numberRows + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // prop: numberColumns + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + // this will override the placement option on the Legend object + this.placement = "outside"; + $.extend(true, this, options); + }; + + // called with scope of legend + $.jqplot.MekkoLegendRenderer.prototype.draw = function() { + var legend = this; + if (this.show) { + var series = this._series; + var ss = 'position:absolute;'; + ss += (this.background) ? 'background:'+this.background+';' : ''; + ss += (this.border) ? 'border:'+this.border+';' : ''; + ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; + ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; + this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); + // Mekko charts legends don't go by number of series, but by number of data points + // in the series. Refactor things here for that. + + var pad = false, + reverse = true, // mekko charts are always stacked, so reverse + nr, nc; + var s = series[0]; + var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); + + if (s.show) { + var pd = s.data; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(pd.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(pd.length/this.numberColumns); + } + else { + nr = pd.length; + nc = 1; + } + + var i, j, tr, td1, td2, lt, rs, color; + var idx = 0; + + for (i=0; i<nr; i++) { + if (reverse){ + tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); + } + else{ + tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); + } + for (j=0; j<nc; j++) { + if (idx < pd.length) { + lt = this.labels[idx] || pd[idx][0].toString(); + color = colorGenerator.next(); + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ + '</div></td>'); + td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + td2.prependTo(tr); + td1.prependTo(tr); + } + else { + td1.appendTo(tr); + td2.appendTo(tr); + } + pad = true; + } + idx++; + } + } + + tr = null; + td1 = null; + td2 = null; + } + } + return this._elem; + }; + + $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) { + if (this.show) { + // fake a grid for positioning + var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; + if (this.placement == 'insideGrid') { + switch (this.location) { + case 'nw': + var a = grid._left + this.xoffset; + var b = grid._top + this.yoffset; + this._elem.css('left', a); + this._elem.css('top', b); + break; + case 'n': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = grid._top + this.yoffset; + this._elem.css('left', a); + this._elem.css('top', b); + break; + case 'ne': + var a = offsets.right + this.xoffset; + var b = grid._top + this.yoffset; + this._elem.css({right:a, top:b}); + break; + case 'e': + var a = offsets.right + this.xoffset; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({right:a, top:b}); + break; + case 'se': + var a = offsets.right + this.xoffset; + var b = offsets.bottom + this.yoffset; + this._elem.css({right:a, bottom:b}); + break; + case 's': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = offsets.bottom + this.yoffset; + this._elem.css({left:a, bottom:b}); + break; + case 'sw': + var a = grid._left + this.xoffset; + var b = offsets.bottom + this.yoffset; + this._elem.css({left:a, bottom:b}); + break; + case 'w': + var a = grid._left + this.xoffset; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({left:a, top:b}); + break; + default: // same as 'se' + var a = grid._right - this.xoffset; + var b = grid._bottom + this.yoffset; + this._elem.css({right:a, bottom:b}); + break; + } + + } + else { + switch (this.location) { + case 'nw': + var a = this._plotDimensions.width - grid._left + this.xoffset; + var b = grid._top + this.yoffset; + this._elem.css('right', a); + this._elem.css('top', b); + break; + case 'n': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = this._plotDimensions.height - grid._top + this.yoffset; + this._elem.css('left', a); + this._elem.css('bottom', b); + break; + case 'ne': + var a = this._plotDimensions.width - offsets.right + this.xoffset; + var b = grid._top + this.yoffset; + this._elem.css({left:a, top:b}); + break; + case 'e': + var a = this._plotDimensions.width - offsets.right + this.xoffset; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({left:a, top:b}); + break; + case 'se': + var a = this._plotDimensions.width - offsets.right + this.xoffset; + var b = offsets.bottom + this.yoffset; + this._elem.css({left:a, bottom:b}); + break; + case 's': + var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; + var b = this._plotDimensions.height - offsets.bottom + this.yoffset; + this._elem.css({left:a, top:b}); + break; + case 'sw': + var a = this._plotDimensions.width - grid._left + this.xoffset; + var b = offsets.bottom + this.yoffset; + this._elem.css({right:a, bottom:b}); + break; + case 'w': + var a = this._plotDimensions.width - grid._left + this.xoffset; + var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; + this._elem.css({right:a, top:b}); + break; + default: // same as 'se' + var a = grid._right - this.xoffset; + var b = grid._bottom + this.yoffset; + this._elem.css({right:a, bottom:b}); + break; + } + } + } + }; + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.MekkoRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer; + options.legend.renderer = $.jqplot.MekkoLegendRenderer; + options.legend.preDraw = true; + } + } + + $.jqplot.preInitHooks.push(preInit); + +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.min.js new file mode 100644 index 0000000..18dc3a1 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mekkoRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(b){b.jqplot.MekkoRenderer=function(){this.shapeRenderer=new b.jqplot.ShapeRenderer();this.borderColor=null;this.showBorders=true};b.jqplot.MekkoRenderer.prototype.init=function(c,e){this.fill=false;this.fillRect=true;this.strokeRect=true;this.shadow=false;this._xwidth=0;this._xstart=0;b.extend(true,this.renderer,c);var d={lineJoin:"miter",lineCap:"butt",isarc:false,fillRect:this.fillRect,strokeRect:this.strokeRect};this.renderer.shapeRenderer.init(d);e.axes.x2axis._series.push(this);this._type="mekko"};b.jqplot.MekkoRenderer.prototype.setGridData=function(h){var e=this._xaxis.series_u2p;var c=this._yaxis.series_u2p;var g=this._plotData;this.gridData=[];this._xwidth=e(this._sumy)-e(0);if(this.index>0){this._xstart=h.series[this.index-1]._xstart+h.series[this.index-1]._xwidth}var l=this.canvas.getHeight();var d=0;var k;var j;for(var f=0;f<g.length;f++){if(g[f]!=null){d+=g[f][1];k=l-(d/this._sumy*l);j=g[f][1]/this._sumy*l;this.gridData.push([this._xstart,k,this._xwidth,j])}}};b.jqplot.MekkoRenderer.prototype.makeGridData=function(f,g){var d=this._xaxis.series_u2p;var l=this.canvas.getHeight();var c=0;var j;var h;var k=[];for(var e=0;e<f.length;e++){if(f[e]!=null){c+=f[e][1];j=l-(c/this._sumy*l);h=f[e][1]/this._sumy*l;k.push([this._xstart,j,this._xwidth,h])}}return k};b.jqplot.MekkoRenderer.prototype.draw=function(c,h,d){var e;var g=(d!=undefined)?d:{};var f=(g.showLine!=undefined)?g.showLine:this.showLine;var j=new b.jqplot.ColorGenerator(this.seriesColors);c.save();if(h.length){if(f){for(e=0;e<h.length;e++){g.fillStyle=j.next();if(this.renderer.showBorders){g.strokeStyle=this.renderer.borderColor}else{g.strokeStyle=g.fillStyle}this.renderer.shapeRenderer.draw(c,h[e],g)}}}c.restore()};b.jqplot.MekkoRenderer.prototype.drawShadow=function(c,e,d){};b.jqplot.MekkoLegendRenderer=function(){};b.jqplot.MekkoLegendRenderer.prototype.init=function(c){this.numberRows=null;this.numberColumns=null;this.placement="outside";b.extend(true,this,c)};b.jqplot.MekkoLegendRenderer.prototype.draw=function(){var f=this;if(this.show){var o=this._series;var r="position:absolute;";r+=(this.background)?"background:"+this.background+";":"";r+=(this.border)?"border:"+this.border+";":"";r+=(this.fontSize)?"font-size:"+this.fontSize+";":"";r+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";r+=(this.textColor)?"color:"+this.textColor+";":"";this._elem=b('<table class="jqplot-table-legend" style="'+r+'"></table>');var w=false,n=true,c,l;var p=o[0];var d=new b.jqplot.ColorGenerator(p.seriesColors);if(p.show){var x=p.data;if(this.numberRows){c=this.numberRows;if(!this.numberColumns){l=Math.ceil(x.length/c)}else{l=this.numberColumns}}else{if(this.numberColumns){l=this.numberColumns;c=Math.ceil(x.length/this.numberColumns)}else{c=x.length;l=1}}var v,u,e,h,g,k,m,t;var q=0;for(v=0;v<c;v++){if(n){e=b('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem)}else{e=b('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem)}for(u=0;u<l;u++){if(q<x.length){k=this.labels[q]||x[q][0].toString();t=d.next();if(!n){if(v>0){w=true}else{w=false}}else{if(v==c-1){w=false}else{w=true}}m=(w)?this.rowSpacing:"0";h=b('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+m+';"><div><div class="jqplot-table-legend-swatch" style="border-color:'+t+';"></div></div></td>');g=b('<td class="jqplot-table-legend" style="padding-top:'+m+';"></td>');if(this.escapeHtml){g.text(k)}else{g.html(k)}if(n){g.prependTo(e);h.prependTo(e)}else{h.appendTo(e);g.appendTo(e)}w=true}q++}}e=null;h=null;g=null}}return this._elem};b.jqplot.MekkoLegendRenderer.prototype.pack=function(f){if(this.show){var e={_top:f.top,_left:f.left,_right:f.right,_bottom:this._plotDimensions.height-f.bottom};if(this.placement=="insideGrid"){switch(this.location){case"nw":var d=e._left+this.xoffset;var c=e._top+this.yoffset;this._elem.css("left",d);this._elem.css("top",c);break;case"n":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=e._top+this.yoffset;this._elem.css("left",d);this._elem.css("top",c);break;case"ne":var d=f.right+this.xoffset;var c=e._top+this.yoffset;this._elem.css({right:d,top:c});break;case"e":var d=f.right+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({right:d,top:c});break;case"se":var d=f.right+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({right:d,bottom:c});break;case"s":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"sw":var d=e._left+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"w":var d=e._left+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({left:d,top:c});break;default:var d=e._right-this.xoffset;var c=e._bottom+this.yoffset;this._elem.css({right:d,bottom:c});break}}else{switch(this.location){case"nw":var d=this._plotDimensions.width-e._left+this.xoffset;var c=e._top+this.yoffset;this._elem.css("right",d);this._elem.css("top",c);break;case"n":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=this._plotDimensions.height-e._top+this.yoffset;this._elem.css("left",d);this._elem.css("bottom",c);break;case"ne":var d=this._plotDimensions.width-f.right+this.xoffset;var c=e._top+this.yoffset;this._elem.css({left:d,top:c});break;case"e":var d=this._plotDimensions.width-f.right+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({left:d,top:c});break;case"se":var d=this._plotDimensions.width-f.right+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({left:d,bottom:c});break;case"s":var d=(f.left+(this._plotDimensions.width-f.right))/2-this.getWidth()/2;var c=this._plotDimensions.height-f.bottom+this.yoffset;this._elem.css({left:d,top:c});break;case"sw":var d=this._plotDimensions.width-e._left+this.xoffset;var c=f.bottom+this.yoffset;this._elem.css({right:d,bottom:c});break;case"w":var d=this._plotDimensions.width-e._left+this.xoffset;var c=(f.top+(this._plotDimensions.height-f.bottom))/2-this.getHeight()/2;this._elem.css({right:d,top:c});break;default:var d=e._right-this.xoffset;var c=e._bottom+this.yoffset;this._elem.css({right:d,bottom:c});break}}}};function a(g,f,d){d=d||{};d.axesDefaults=d.axesDefaults||{};d.legend=d.legend||{};d.seriesDefaults=d.seriesDefaults||{};var c=false;if(d.seriesDefaults.renderer==b.jqplot.MekkoRenderer){c=true}else{if(d.series){for(var e=0;e<d.series.length;e++){if(d.series[e].renderer==b.jqplot.MekkoRenderer){c=true}}}}if(c){d.axesDefaults.renderer=b.jqplot.MekkoAxisRenderer;d.legend.renderer=b.jqplot.MekkoLegendRenderer;d.legend.preDraw=true}}b.jqplot.preInitHooks.push(a)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.js b/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.js new file mode 100644 index 0000000..8bd899d --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.js @@ -0,0 +1,1029 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.MeterGaugeRenderer + * Plugin renderer to draw a meter gauge chart. + * + * Data consists of a single series with 1 data point to position the gauge needle. + * + * To use this renderer, you need to include the + * meter gauge renderer plugin, for example: + * + * > <script type="text/javascript" src="plugins/jqplot.meterGaugeRenderer.js"></script> + * + * Properties described here are passed into the $.jqplot function + * as options on the series renderer. For example: + * + * > plot0 = $.jqplot('chart0',[[18]],{ + * > title: 'Network Speed', + * > seriesDefaults: { + * > renderer: $.jqplot.MeterGaugeRenderer, + * > rendererOptions: { + * > label: 'MB/s' + * > } + * > } + * > }); + * + * A meterGauge plot does not support events. + */ + $.jqplot.MeterGaugeRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.MeterGaugeRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.MeterGaugeRenderer.prototype.constructor = $.jqplot.MeterGaugeRenderer; + + // called with scope of a series + $.jqplot.MeterGaugeRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: diameter + // Outer diameter of the meterGauge, auto computed by default + this.diameter = null; + // prop: padding + // padding between the meterGauge and plot edges, auto + // calculated by default. + this.padding = null; + // prop: shadowOffset + // offset of the shadow from the gauge ring and offset of + // each succesive stroke of the shadow from the last. + this.shadowOffset = 2; + // prop: shadowAlpha + // transparency of the shadow (0 = transparent, 1 = opaque) + this.shadowAlpha = 0.07; + // prop: shadowDepth + // number of strokes to apply to the shadow, + // each stroke offset shadowOffset from the last. + this.shadowDepth = 4; + // prop: background + // background color of the inside of the gauge. + this.background = "#efefef"; + // prop: ringColor + // color of the outer ring, hub, and needle of the gauge. + this.ringColor = "#BBC6D0"; + // needle color not implemented yet. + this.needleColor = "#C3D3E5"; + // prop: tickColor + // color of the tick marks around the gauge. + this.tickColor = "#989898"; + // prop: ringWidth + // width of the ring around the gauge. Auto computed by default. + this.ringWidth = null; + // prop: min + // Minimum value on the gauge. Auto computed by default + this.min; + // prop: max + // Maximum value on the gauge. Auto computed by default + this.max; + // prop: ticks + // Array of tick values. Auto computed by default. + this.ticks = []; + // prop: showTicks + // true to show ticks around gauge. + this.showTicks = true; + // prop: showTickLabels + // true to show tick labels next to ticks. + this.showTickLabels = true; + // prop: label + // A gauge label like 'kph' or 'Volts' + this.label = null; + // prop: labelHeightAdjust + // Number of Pixels to offset the label up (-) or down (+) from its default position. + this.labelHeightAdjust = 0; + // prop: labelPosition + // Where to position the label, either 'inside' or 'bottom'. + this.labelPosition = 'inside'; + // prop: intervals + // Array of ranges to be drawn around the gauge. + // Array of form: + // > [value1, value2, ...] + // indicating the values for the first, second, ... intervals. + this.intervals = []; + // prop: intervalColors + // Array of colors to use for the intervals. + this.intervalColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"]; + // prop: intervalInnerRadius + // Radius of the inner circle of the interval ring. + this.intervalInnerRadius = null; + // prop: intervalOuterRadius + // Radius of the outer circle of the interval ring. + this.intervalOuterRadius = null; + this.tickRenderer = $.jqplot.MeterGaugeTickRenderer; + // ticks spaced every 1, 2, 2.5, 5, 10, 20, .1, .2, .25, .5, etc. + this.tickPositions = [1, 2, 2.5, 5, 10]; + // prop: tickSpacing + // Degrees between ticks. This is a target number, if + // incompatible span and ticks are supplied, a suitable + // spacing close to this value will be computed. + this.tickSpacing = 30; + this.numberMinorTicks = null; + // prop: hubRadius + // Radius of the hub at the bottom center of gauge which the needle attaches to. + // Auto computed by default + this.hubRadius = null; + // prop: tickPadding + // padding of the tick marks to the outer ring and the tick labels to marks. + // Auto computed by default. + this.tickPadding = null; + // prop: needleThickness + // Maximum thickness the needle. Auto computed by default. + this.needleThickness = null; + // prop: needlePad + // Padding between needle and inner edge of the ring when the needle is at the min or max gauge value. + this.needlePad = 6; + // prop: pegNeedle + // True will stop needle just below/above the min/max values if data is below/above min/max, + // as if the meter is "pegged". + this.pegNeedle = true; + this._type = 'meterGauge'; + + $.extend(true, this, options); + this.type = null; + this.numberTicks = null; + this.tickInterval = null; + // span, the sweep (in degrees) from min to max. This gauge is + // a semi-circle. + this.span = 180; + // get rid of this nonsense + // this.innerSpan = this.span; + if (this.type == 'circular') { + this.semiCircular = false; + } + else if (this.type != 'circular') { + this.semiCircular = true; + } + else { + this.semiCircular = (this.span <= 180) ? true : false; + } + this._tickPoints = []; + // reference to label element. + this._labelElem = null; + + // start the gauge at the beginning of the span + this.startAngle = (90 + (360 - this.span)/2) * Math.PI/180; + this.endAngle = (90 - (360 - this.span)/2) * Math.PI/180; + + this.setmin = !!(this.min == null); + this.setmax = !!(this.max == null); + + // if given intervals and is an array of values, create labels and colors. + if (this.intervals.length) { + if (this.intervals[0].length == null || this.intervals.length == 1) { + for (var i=0; i<this.intervals.length; i++) { + this.intervals[i] = [this.intervals[i], this.intervals[i], this.intervalColors[i]]; + } + } + else if (this.intervals[0].length == 2) { + for (i=0; i<this.intervals.length; i++) { + this.intervals[i] = [this.intervals[i][0], this.intervals[i][1], this.intervalColors[i]]; + } + } + } + + // compute min, max and ticks if not supplied: + if (this.ticks.length) { + if (this.ticks[0].length == null || this.ticks[0].length == 1) { + for (var i=0; i<this.ticks.length; i++) { + this.ticks[i] = [this.ticks[i], this.ticks[i]]; + } + } + this.min = (this.min == null) ? this.ticks[0][0] : this.min; + this.max = (this.max == null) ? this.ticks[this.ticks.length-1][0] : this.max; + this.setmin = false; + this.setmax = false; + this.numberTicks = this.ticks.length; + this.tickInterval = this.ticks[1][0] - this.ticks[0][0]; + this.tickFactor = Math.floor(parseFloat((Math.log(this.tickInterval)/Math.log(10)).toFixed(11))); + // use the first interal to calculate minor ticks; + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); + if (!this.numberMinorTicks) { + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); + } + if (!this.numberMinorTicks) { + this.numberMinorTicks = 1; + } + } + + else if (this.intervals.length) { + this.min = (this.min == null) ? 0 : this.min; + this.setmin = false; + if (this.max == null) { + if (this.intervals[this.intervals.length-1][0] >= this.data[0][1]) { + this.max = this.intervals[this.intervals.length-1][0]; + this.setmax = false; + } + } + else { + this.setmax = false; + } + } + + else { + // no ticks and no intervals supplied, put needle in middle + this.min = (this.min == null) ? 0 : this.min; + this.setmin = false; + if (this.max == null) { + this.max = this.data[0][1] * 1.25; + this.setmax = true; + } + else { + this.setmax = false; + } + } + }; + + $.jqplot.MeterGaugeRenderer.prototype.setGridData = function(plot) { + // set gridData property. This will hold angle in radians of each data point. + var stack = []; + var td = []; + var sa = this.startAngle; + for (var i=0; i<this.data.length; i++){ + stack.push(this.data[i][1]); + td.push([this.data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + } + this.gridData = td; + }; + + $.jqplot.MeterGaugeRenderer.prototype.makeGridData = function(data, plot) { + var stack = []; + var td = []; + var sa = this.startAngle; + for (var i=0; i<data.length; i++){ + stack.push(data[i][1]); + td.push([data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + } + return td; + }; + + + function getnmt(pos, interval, fact) { + var temp; + for (var i=pos.length-1; i>=0; i--) { + temp = interval/(pos[i] * Math.pow(10, fact)); + if (temp == 4 || temp == 5) { + return temp - 1; + } + } + return null; + } + + // called with scope of series + $.jqplot.MeterGaugeRenderer.prototype.draw = function (ctx, gd, options) { + var i; + var opts = (options != undefined) ? options : {}; + // offset and direction of offset due to legend placement + var offx = 0; + var offy = 0; + var trans = 1; + if (options.legendInfo && options.legendInfo.placement == 'inside') { + var li = options.legendInfo; + switch (li.location) { + case 'nw': + offx = li.width + li.xoffset; + break; + case 'w': + offx = li.width + li.xoffset; + break; + case 'sw': + offx = li.width + li.xoffset; + break; + case 'ne': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'e': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'se': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'n': + offy = li.height + li.yoffset; + break; + case 's': + offy = li.height + li.yoffset; + trans = -1; + break; + default: + break; + } + } + + + + // pre-draw so can get its dimensions. + if (this.label) { + this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>'); + this.canvas._elem.after(this._labelElem); + } + + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var cw = ctx.canvas.width; + var ch = ctx.canvas.height; + if (this.padding == null) { + this.padding = Math.round(Math.min(cw, ch)/30); + } + var w = cw - offx - 2 * this.padding; + var h = ch - offy - 2 * this.padding; + if (this.labelPosition == 'bottom' && this.label) { + h -= this._labelElem.outerHeight(true); + } + var mindim = Math.min(w,h); + var d = mindim; + + if (!this.diameter) { + if (this.semiCircular) { + if ( w >= 2*h) { + if (!this.ringWidth) { + this.ringWidth = 2*h/35; + } + this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); + this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad; + this.diameter = 2 * (h - 2*this.innerPad); + } + else { + if (!this.ringWidth) { + this.ringWidth = w/35; + } + this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); + this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad; + this.diameter = w - 2*this.innerPad - this.ringWidth - this.padding; + } + // center taking into account legend and over draw for gauge bottom below hub. + // this will be center of hub. + this._center = [(cw - trans * offx)/2 + trans * offx, (ch + trans*offy - this.padding - this.ringWidth - this.innerPad)]; + } + else { + if (!this.ringWidth) { + this.ringWidth = d/35; + } + this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); + this.innerPad = 0; + this.diameter = d - this.ringWidth; + // center in middle of canvas taking into account legend. + // will be center of hub. + this._center = [(cw-trans*offx)/2 + trans * offx, (ch-trans*offy)/2 + trans * offy]; + } + if (this._labelElem && this.labelPosition == 'bottom') { + this._center[1] -= this._labelElem.outerHeight(true); + } + + } + + this._radius = this.diameter/2; + + this.tickSpacing = 6000/this.diameter; + + if (!this.hubRadius) { + this.hubRadius = this.diameter/18; + } + + this.shadowOffset = 0.5 + this.ringWidth/9; + this.shadowWidth = this.ringWidth*1; + + this.tickPadding = 3 + Math.pow(this.diameter/20, 0.7); + this.tickOuterRadius = this._radius - this.ringWidth/2 - this.tickPadding; + this.tickLength = (this.showTicks) ? this._radius/13 : 0; + + if (this.ticks.length == 0) { + // no ticks, lets make some. + var max = this.max, + min = this.min, + setmax = this.setmax, + setmin = this.setmin, + ti = (max - min) * this.tickSpacing / this.span; + var tf = Math.floor(parseFloat((Math.log(ti)/Math.log(10)).toFixed(11))); + var tp = (ti/Math.pow(10, tf)); + (tp > 2 && tp <= 2.5) ? tp = 2.5 : tp = Math.ceil(tp); + var t = this.tickPositions; + var tpindex, nt; + + for (i=0; i<t.length; i++) { + if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { + ti = t[i]*Math.pow(10, tf); + tpindex = i; + } + } + + for (i=0; i<t.length; i++) { + if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { + ti = t[i]*Math.pow(10, tf); + nt = Math.ceil((max - min) / ti); + } + } + + // both max and min are free + if (setmax && setmin) { + var tmin = (min > 0) ? min - min % ti : min - min % ti - ti; + if (!this.forceZero) { + var diff = Math.min(min - tmin, 0.8*ti); + var ntp = Math.floor(diff/t[tpindex]); + if (ntp > 1) { + tmin = tmin + t[tpindex] * (ntp-1); + if (parseInt(tmin, 10) != tmin && parseInt(tmin-t[tpindex], 10) == tmin-t[tpindex]) { + tmin = tmin - t[tpindex]; + } + } + } + if (min == tmin) { + min -= ti; + } + else { + // tmin should always be lower than dataMin + if (min - tmin > 0.23*ti) { + min = tmin; + } + else { + min = tmin -ti; + nt += 1; + } + } + nt += 1; + var tmax = min + (nt - 1) * ti; + if (max >= tmax) { + tmax += ti; + nt += 1; + } + // now tmax should always be mroe than dataMax + if (tmax - max < 0.23*ti) { + tmax += ti; + nt += 1; + } + this.max = max = tmax; + this.min = min; + + this.tickInterval = ti; + this.numberTicks = nt; + var it; + for (i=0; i<nt; i++) { + it = parseFloat((min+i*ti).toFixed(11)); + this.ticks.push([it, it]); + } + this.max = this.ticks[nt-1][1]; + + this.tickFactor = tf; + // determine number of minor ticks + + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); + + if (!this.numberMinorTicks) { + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); + } + } + // max is free, min is fixed + else if (setmax) { + var tmax = min + (nt - 1) * ti; + if (max >= tmax) { + max = tmax + ti; + nt += 1; + } + else { + max = tmax; + } + + this.tickInterval = this.tickInterval || ti; + this.numberTicks = this.numberTicks || nt; + var it; + for (i=0; i<this.numberTicks; i++) { + it = parseFloat((min+i*this.tickInterval).toFixed(11)); + this.ticks.push([it, it]); + } + this.max = this.ticks[this.numberTicks-1][1]; + + this.tickFactor = tf; + // determine number of minor ticks + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); + + if (!this.numberMinorTicks) { + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); + } + } + + // not setting max or min + if (!setmax && !setmin) { + var range = this.max - this.min; + tf = Math.floor(parseFloat((Math.log(range)/Math.log(10)).toFixed(11))) - 1; + var nticks = [5,6,4,7,3,8,9,10,2], res, numticks, nonSigDigits=0, sigRange; + // check to see how many zeros are at the end of the range + if (range > 1) { + var rstr = String(range); + if (rstr.search(/\./) == -1) { + var pos = rstr.search(/0+$/); + nonSigDigits = (pos > 0) ? rstr.length - pos - 1 : 0; + } + } + sigRange = range/Math.pow(10, nonSigDigits); + for (i=0; i<nticks.length; i++) { + res = sigRange/(nticks[i]-1); + if (res == parseInt(res, 10)) { + this.numberTicks = nticks[i]; + this.tickInterval = range/(this.numberTicks-1); + this.tickFactor = tf+1; + break; + } + } + var it; + for (i=0; i<this.numberTicks; i++) { + it = parseFloat((this.min+i*this.tickInterval).toFixed(11)); + this.ticks.push([it, it]); + } + // determine number of minor ticks + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); + + if (!this.numberMinorTicks) { + this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); + } + + if (!this.numberMinorTicks) { + this.numberMinorTicks = 1; + var nums = [4, 5, 3, 6, 2]; + for (i=0; i<5; i++) { + var temp = this.tickInterval/nums[i]; + if (temp == parseInt(temp, 10)) { + this.numberMinorTicks = nums[i]-1; + break; + } + } + } + } + } + + + var r = this._radius, + sa = this.startAngle, + ea = this.endAngle, + pi = Math.PI, + hpi = Math.PI/2; + + if (this.semiCircular) { + var overAngle = Math.atan(this.innerPad/r), + outersa = this.outerStartAngle = sa - overAngle, + outerea = this.outerEndAngle = ea + overAngle, + hubsa = this.hubStartAngle = sa - Math.atan(this.innerPad/this.hubRadius*2), + hubea = this.hubEndAngle = ea + Math.atan(this.innerPad/this.hubRadius*2); + + ctx.save(); + + ctx.translate(this._center[0], this._center[1]); + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + + // draw the innerbackground + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = this.background; + ctx.arc(0, 0, r, outersa, outerea, false); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + + // draw the shadow + // the outer ring. + var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; + ctx.save(); + for (var i=0; i<this.shadowDepth; i++) { + ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); + ctx.beginPath(); + ctx.strokeStyle = shadowColor; + ctx.lineWidth = this.shadowWidth; + ctx.arc(0 ,0, r, outersa, outerea, false); + ctx.closePath(); + ctx.stroke(); + } + ctx.restore(); + + // the inner hub. + ctx.save(); + var tempd = parseInt((this.shadowDepth+1)/2, 10); + for (var i=0; i<tempd; i++) { + ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); + ctx.beginPath(); + ctx.fillStyle = shadowColor; + ctx.arc(0 ,0, this.hubRadius, hubsa, hubea, false); + ctx.closePath(); + ctx.fill(); + } + ctx.restore(); + + // draw the outer ring. + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = this.ringColor; + ctx.lineWidth = this.ringWidth; + ctx.arc(0 ,0, r, outersa, outerea, false); + ctx.closePath(); + ctx.stroke(); + ctx.restore(); + + // draw the hub + + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = this.ringColor; + ctx.arc(0 ,0, this.hubRadius,hubsa, hubea, false); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + + // draw the ticks + if (this.showTicks) { + ctx.save(); + var orad = this.tickOuterRadius, + tl = this.tickLength, + mtl = tl/2, + nmt = this.numberMinorTicks, + ts = this.span * Math.PI / 180 / (this.ticks.length-1), + mts = ts/(nmt + 1); + + for (i = 0; i<this.ticks.length; i++) { + ctx.beginPath(); + ctx.lineWidth = 1.5 + this.diameter/360; + ctx.strokeStyle = this.ringColor; + var wps = ts*i+sa; + ctx.moveTo(-orad * Math.cos(ts*i+sa), orad * Math.sin(ts*i+sa)); + ctx.lineTo(-(orad-tl) * Math.cos(ts*i+sa), (orad - tl) * Math.sin(ts*i+sa)); + this._tickPoints.push([(orad-tl) * Math.cos(ts*i+sa) + this._center[0] + this.canvas._offsets.left, (orad - tl) * Math.sin(ts*i+sa) + this._center[1] + this.canvas._offsets.top, ts*i+sa]); + ctx.stroke(); + ctx.lineWidth = 1.0 + this.diameter/440; + if (i<this.ticks.length-1) { + for (var j=1; j<=nmt; j++) { + ctx.beginPath(); + ctx.moveTo(-orad * Math.cos(ts*i+mts*j+sa), orad * Math.sin(ts*i+mts*j+sa)); + ctx.lineTo(-(orad-mtl) * Math.cos(ts*i+mts*j+sa), (orad-mtl) * Math.sin(ts*i+mts*j+sa)); + ctx.stroke(); + } + } + } + ctx.restore(); + } + + // draw the tick labels + if (this.showTickLabels) { + var elem, l, t, ew, eh, dim, maxdim=0; + var tp = this.tickPadding * (1 - 1/(this.diameter/80+1)); + for (i=0; i<this.ticks.length; i++) { + elem = $('<div class="jqplot-meterGauge-tick" style="position:absolute;">'+this.ticks[i][1]+'</div>'); + this.canvas._elem.after(elem); + ew = elem.outerWidth(true); + eh = elem.outerHeight(true); + l = this._tickPoints[i][0] - ew * (this._tickPoints[i][2]-Math.PI)/Math.PI - tp * Math.cos(this._tickPoints[i][2]); + t = this._tickPoints[i][1] - eh/2 + eh/2 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) + tp/3 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) ; + // t = this._tickPoints[i][1] - eh/2 - eh/2 * Math.sin(this._tickPoints[i][2]) - tp/2 * Math.sin(this._tickPoints[i][2]); + elem.css({left:l, top:t, color: this.tickColor}); + dim = ew*Math.cos(this._tickPoints[i][2]) + eh*Math.sin(Math.PI/2+this._tickPoints[i][2]/2); + maxdim = (dim > maxdim) ? dim : maxdim; + } + } + + // draw the gauge label + if (this.label && this.labelPosition == 'inside') { + var l = this._center[0] + this.canvas._offsets.left; + var tp = this.tickPadding * (1 - 1/(this.diameter/80+1)); + var t = 0.5*(this._center[1] + this.canvas._offsets.top - this.hubRadius) + 0.5*(this._center[1] + this.canvas._offsets.top - this.tickOuterRadius + this.tickLength + tp) + this.labelHeightAdjust; + // this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>'); + // this.canvas._elem.after(this._labelElem); + l -= this._labelElem.outerWidth(true)/2; + t -= this._labelElem.outerHeight(true)/2; + this._labelElem.css({left:l, top:t}); + } + + else if (this.label && this.labelPosition == 'bottom') { + var l = this._center[0] + this.canvas._offsets.left - this._labelElem.outerWidth(true)/2; + var t = this._center[1] + this.canvas._offsets.top + this.innerPad + this.ringWidth + this.padding + this.labelHeightAdjust; + this._labelElem.css({left:l, top:t}); + + } + + // draw the intervals + + ctx.save(); + var inner = this.intervalInnerRadius || this.hubRadius * 1.5; + if (this.intervalOuterRadius == null) { + if (this.showTickLabels) { + var outer = (this.tickOuterRadius - this.tickLength - this.tickPadding - this.diameter/8); + } + else { + var outer = (this.tickOuterRadius - this.tickLength - this.diameter/16); + } + } + else { + var outer = this.intervalOuterRadius; + } + var range = this.max - this.min; + var intrange = this.intervals[this.intervals.length-1] - this.min; + var start, end, span = this.span*Math.PI/180; + for (i=0; i<this.intervals.length; i++) { + start = (i == 0) ? sa : sa + (this.intervals[i-1][0] - this.min)*span/range; + if (start < 0) { + start = 0; + } + end = sa + (this.intervals[i][0] - this.min)*span/range; + if (end < 0) { + end = 0; + } + ctx.beginPath(); + ctx.fillStyle = this.intervals[i][2]; + ctx.arc(0, 0, inner, start, end, false); + ctx.lineTo(outer*Math.cos(end), outer*Math.sin(end)); + ctx.arc(0, 0, outer, end, start, true); + ctx.lineTo(inner*Math.cos(start), inner*Math.sin(start)); + ctx.closePath(); + ctx.fill(); + } + ctx.restore(); + + // draw the needle + var datapoint = this.data[0][1]; + var dataspan = this.max - this.min; + if (this.pegNeedle) { + if (this.data[0][1] > this.max + dataspan*3/this.span) { + datapoint = this.max + dataspan*3/this.span; + } + if (this.data[0][1] < this.min - dataspan*3/this.span) { + datapoint = this.min - dataspan*3/this.span; + } + } + var dataang = (datapoint - this.min)/dataspan * this.span * Math.PI/180 + this.startAngle; + + + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = this.ringColor; + ctx.strokeStyle = this.ringColor; + this.needleLength = (this.tickOuterRadius - this.tickLength) * 0.85; + this.needleThickness = (this.needleThickness < 2) ? 2 : this.needleThickness; + var endwidth = this.needleThickness * 0.4; + + + var dl = this.needleLength/10; + var dt = (this.needleThickness - endwidth)/10; + var templ; + for (var i=0; i<10; i++) { + templ = this.needleThickness - i*dt; + ctx.moveTo(dl*i*Math.cos(dataang), dl*i*Math.sin(dataang)); + ctx.lineWidth = templ; + ctx.lineTo(dl*(i+1)*Math.cos(dataang), dl*(i+1)*Math.sin(dataang)); + ctx.stroke(); + } + + ctx.restore(); + } + else { + this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy]; + } + }; + + $.jqplot.MeterGaugeAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.MeterGaugeAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.MeterGaugeAxisRenderer.prototype.constructor = $.jqplot.MeterGaugeAxisRenderer; + + + // There are no traditional axes on a gauge chart. We just need to provide + // dummy objects with properties so the plot will render. + // called with scope of axis object. + $.jqplot.MeterGaugeAxisRenderer.prototype.init = function(options){ + // + this.tickRenderer = $.jqplot.MeterGaugeTickRenderer; + $.extend(true, this, options); + // I don't think I'm going to need _dataBounds here. + // have to go Axis scaling in a way to fit chart onto plot area + // and provide u2p and p2u functionality for mouse cursor, etc. + // for convienence set _dataBounds to 0 and 100 and + // set min/max to 0 and 100. + this._dataBounds = {min:0, max:100}; + this.min = 0; + this.max = 100; + this.showTicks = false; + this.ticks = []; + this.showMark = false; + this.show = false; + }; + + $.jqplot.MeterGaugeLegendRenderer = function(){ + $.jqplot.TableLegendRenderer.call(this); + }; + + $.jqplot.MeterGaugeLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.MeterGaugeLegendRenderer.prototype.constructor = $.jqplot.MeterGaugeLegendRenderer; + + /** + * Class: $.jqplot.MeterGaugeLegendRenderer + *Meter gauges don't typically have a legend, this overrides the default legend renderer. + */ + $.jqplot.MeterGaugeLegendRenderer.prototype.init = function(options) { + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + $.extend(true, this, options); + }; + + // called with context of legend + $.jqplot.MeterGaugeLegendRenderer.prototype.draw = function() { + if (this.show) { + var series = this._series; + var ss = 'position:absolute;'; + ss += (this.background) ? 'background:'+this.background+';' : ''; + ss += (this.border) ? 'border:'+this.border+';' : ''; + ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; + ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; + ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; + ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; + ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; + ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; + ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; + this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); + // MeterGauge charts legends don't go by number of series, but by number of data points + // in the series. Refactor things here for that. + + var pad = false, + reverse = false, + nr, nc; + var s = series[0]; + + if (s.show) { + var pd = s.data; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(pd.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(pd.length/this.numberColumns); + } + else { + nr = pd.length; + nc = 1; + } + + var i, j, tr, td1, td2, lt, rs, color; + var idx = 0; + + for (i=0; i<nr; i++) { + if (reverse){ + tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); + } + else{ + tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); + } + for (j=0; j<nc; j++) { + if (idx < pd.length){ + // debugger + lt = this.labels[idx] || pd[idx][0].toString(); + color = s.color; + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ + '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ + '</div></td>'); + td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + td2.prependTo(tr); + td1.prependTo(tr); + } + else { + td1.appendTo(tr); + td2.appendTo(tr); + } + pad = true; + } + idx++; + } + } + } + } + return this._elem; + }; + + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + // debugger + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + options.grid = options.grid || {}; + + // only set these if there is a gauge series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.MeterGaugeRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.MeterGaugeRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.MeterGaugeAxisRenderer; + options.legend.renderer = $.jqplot.MeterGaugeLegendRenderer; + options.legend.preDraw = true; + options.grid.background = options.grid.background || 'white'; + options.grid.drawGridlines = false; + options.grid.borderWidth = (options.grid.borderWidth != null) ? options.grid.borderWidth : 0; + options.grid.shadow = (options.grid.shadow != null) ? options.grid.shadow : false; + } + } + + // called with scope of plot + function postParseOptions(options) { + // + } + + $.jqplot.preInitHooks.push(preInit); + $.jqplot.postParseOptionsHooks.push(postParseOptions); + + $.jqplot.MeterGaugeTickRenderer = function() { + $.jqplot.AxisTickRenderer.call(this); + }; + + $.jqplot.MeterGaugeTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); + $.jqplot.MeterGaugeTickRenderer.prototype.constructor = $.jqplot.MeterGaugeTickRenderer; + +})(jQuery); + + diff --git a/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.min.js new file mode 100644 index 0000000..e0f1081 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.meterGaugeRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(c){c.jqplot.MeterGaugeRenderer=function(){c.jqplot.LineRenderer.call(this)};c.jqplot.MeterGaugeRenderer.prototype=new c.jqplot.LineRenderer();c.jqplot.MeterGaugeRenderer.prototype.constructor=c.jqplot.MeterGaugeRenderer;c.jqplot.MeterGaugeRenderer.prototype.init=function(e){this.diameter=null;this.padding=null;this.shadowOffset=2;this.shadowAlpha=0.07;this.shadowDepth=4;this.background="#efefef";this.ringColor="#BBC6D0";this.needleColor="#C3D3E5";this.tickColor="#989898";this.ringWidth=null;this.min;this.max;this.ticks=[];this.showTicks=true;this.showTickLabels=true;this.label=null;this.labelHeightAdjust=0;this.labelPosition="inside";this.intervals=[];this.intervalColors=["#4bb2c5","#EAA228","#c5b47f","#579575","#839557","#958c12","#953579","#4b5de4","#d8b83f","#ff5800","#0085cc","#c747a3","#cddf54","#FBD178","#26B4E3","#bd70c7"];this.intervalInnerRadius=null;this.intervalOuterRadius=null;this.tickRenderer=c.jqplot.MeterGaugeTickRenderer;this.tickPositions=[1,2,2.5,5,10];this.tickSpacing=30;this.numberMinorTicks=null;this.hubRadius=null;this.tickPadding=null;this.needleThickness=null;this.needlePad=6;this.pegNeedle=true;this._type="meterGauge";c.extend(true,this,e);this.type=null;this.numberTicks=null;this.tickInterval=null;this.span=180;if(this.type=="circular"){this.semiCircular=false}else{if(this.type!="circular"){this.semiCircular=true}else{this.semiCircular=(this.span<=180)?true:false}}this._tickPoints=[];this._labelElem=null;this.startAngle=(90+(360-this.span)/2)*Math.PI/180;this.endAngle=(90-(360-this.span)/2)*Math.PI/180;this.setmin=!!(this.min==null);this.setmax=!!(this.max==null);if(this.intervals.length){if(this.intervals[0].length==null||this.intervals.length==1){for(var f=0;f<this.intervals.length;f++){this.intervals[f]=[this.intervals[f],this.intervals[f],this.intervalColors[f]]}}else{if(this.intervals[0].length==2){for(f=0;f<this.intervals.length;f++){this.intervals[f]=[this.intervals[f][0],this.intervals[f][1],this.intervalColors[f]]}}}}if(this.ticks.length){if(this.ticks[0].length==null||this.ticks[0].length==1){for(var f=0;f<this.ticks.length;f++){this.ticks[f]=[this.ticks[f],this.ticks[f]]}}this.min=(this.min==null)?this.ticks[0][0]:this.min;this.max=(this.max==null)?this.ticks[this.ticks.length-1][0]:this.max;this.setmin=false;this.setmax=false;this.numberTicks=this.ticks.length;this.tickInterval=this.ticks[1][0]-this.ticks[0][0];this.tickFactor=Math.floor(parseFloat((Math.log(this.tickInterval)/Math.log(10)).toFixed(11)));this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor);if(!this.numberMinorTicks){this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor-1)}if(!this.numberMinorTicks){this.numberMinorTicks=1}}else{if(this.intervals.length){this.min=(this.min==null)?0:this.min;this.setmin=false;if(this.max==null){if(this.intervals[this.intervals.length-1][0]>=this.data[0][1]){this.max=this.intervals[this.intervals.length-1][0];this.setmax=false}}else{this.setmax=false}}else{this.min=(this.min==null)?0:this.min;this.setmin=false;if(this.max==null){this.max=this.data[0][1]*1.25;this.setmax=true}else{this.setmax=false}}}};c.jqplot.MeterGaugeRenderer.prototype.setGridData=function(j){var f=[];var k=[];var e=this.startAngle;for(var h=0;h<this.data.length;h++){f.push(this.data[h][1]);k.push([this.data[h][0]]);if(h>0){f[h]+=f[h-1]}}var g=Math.PI*2/f[f.length-1];for(var h=0;h<f.length;h++){k[h][1]=f[h]*g}this.gridData=k};c.jqplot.MeterGaugeRenderer.prototype.makeGridData=function(j,k){var f=[];var l=[];var e=this.startAngle;for(var h=0;h<j.length;h++){f.push(j[h][1]);l.push([j[h][0]]);if(h>0){f[h]+=f[h-1]}}var g=Math.PI*2/f[f.length-1];for(var h=0;h<f.length;h++){l[h][1]=f[h]*g}return l};function b(j,f,g){var e;for(var h=j.length-1;h>=0;h--){e=f/(j[h]*Math.pow(10,g));if(e==4||e==5){return e-1}}return null}c.jqplot.MeterGaugeRenderer.prototype.draw=function(X,aC,ap){var aa;var aM=(ap!=undefined)?ap:{};var ai=0;var ah=0;var at=1;if(ap.legendInfo&&ap.legendInfo.placement=="inside"){var aI=ap.legendInfo;switch(aI.location){case"nw":ai=aI.width+aI.xoffset;break;case"w":ai=aI.width+aI.xoffset;break;case"sw":ai=aI.width+aI.xoffset;break;case"ne":ai=aI.width+aI.xoffset;at=-1;break;case"e":ai=aI.width+aI.xoffset;at=-1;break;case"se":ai=aI.width+aI.xoffset;at=-1;break;case"n":ah=aI.height+aI.yoffset;break;case"s":ah=aI.height+aI.yoffset;at=-1;break;default:break}}if(this.label){this._labelElem=c('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+"</div>");this.canvas._elem.after(this._labelElem)}var m=(aM.shadow!=undefined)?aM.shadow:this.shadow;var N=(aM.showLine!=undefined)?aM.showLine:this.showLine;var I=(aM.fill!=undefined)?aM.fill:this.fill;var K=X.canvas.width;var S=X.canvas.height;if(this.padding==null){this.padding=Math.round(Math.min(K,S)/30)}var Q=K-ai-2*this.padding;var ab=S-ah-2*this.padding;if(this.labelPosition=="bottom"&&this.label){ab-=this._labelElem.outerHeight(true)}var L=Math.min(Q,ab);var ad=L;if(!this.diameter){if(this.semiCircular){if(Q>=2*ab){if(!this.ringWidth){this.ringWidth=2*ab/35}this.needleThickness=this.needleThickness||2+Math.pow(this.ringWidth,0.8);this.innerPad=this.ringWidth/2+this.needleThickness/2+this.needlePad;this.diameter=2*(ab-2*this.innerPad)}else{if(!this.ringWidth){this.ringWidth=Q/35}this.needleThickness=this.needleThickness||2+Math.pow(this.ringWidth,0.8);this.innerPad=this.ringWidth/2+this.needleThickness/2+this.needlePad;this.diameter=Q-2*this.innerPad-this.ringWidth-this.padding}this._center=[(K-at*ai)/2+at*ai,(S+at*ah-this.padding-this.ringWidth-this.innerPad)]}else{if(!this.ringWidth){this.ringWidth=ad/35}this.needleThickness=this.needleThickness||2+Math.pow(this.ringWidth,0.8);this.innerPad=0;this.diameter=ad-this.ringWidth;this._center=[(K-at*ai)/2+at*ai,(S-at*ah)/2+at*ah]}if(this._labelElem&&this.labelPosition=="bottom"){this._center[1]-=this._labelElem.outerHeight(true)}}this._radius=this.diameter/2;this.tickSpacing=6000/this.diameter;if(!this.hubRadius){this.hubRadius=this.diameter/18}this.shadowOffset=0.5+this.ringWidth/9;this.shadowWidth=this.ringWidth*1;this.tickPadding=3+Math.pow(this.diameter/20,0.7);this.tickOuterRadius=this._radius-this.ringWidth/2-this.tickPadding;this.tickLength=(this.showTicks)?this._radius/13:0;if(this.ticks.length==0){var A=this.max,aL=this.min,q=this.setmax,aG=this.setmin,au=(A-aL)*this.tickSpacing/this.span;var aw=Math.floor(parseFloat((Math.log(au)/Math.log(10)).toFixed(11)));var an=(au/Math.pow(10,aw));(an>2&&an<=2.5)?an=2.5:an=Math.ceil(an);var T=this.tickPositions;var aA,ak;for(aa=0;aa<T.length;aa++){if(an==T[aa]||aa&&T[aa-1]<an&&an<T[aa]){au=T[aa]*Math.pow(10,aw);aA=aa}}for(aa=0;aa<T.length;aa++){if(an==T[aa]||aa&&T[aa-1]<an&&an<T[aa]){au=T[aa]*Math.pow(10,aw);ak=Math.ceil((A-aL)/au)}}if(q&&aG){var aP=(aL>0)?aL-aL%au:aL-aL%au-au;if(!this.forceZero){var D=Math.min(aL-aP,0.8*au);var o=Math.floor(D/T[aA]);if(o>1){aP=aP+T[aA]*(o-1);if(parseInt(aP,10)!=aP&&parseInt(aP-T[aA],10)==aP-T[aA]){aP=aP-T[aA]}}}if(aL==aP){aL-=au}else{if(aL-aP>0.23*au){aL=aP}else{aL=aP-au;ak+=1}}ak+=1;var E=aL+(ak-1)*au;if(A>=E){E+=au;ak+=1}if(E-A<0.23*au){E+=au;ak+=1}this.max=A=E;this.min=aL;this.tickInterval=au;this.numberTicks=ak;var O;for(aa=0;aa<ak;aa++){O=parseFloat((aL+aa*au).toFixed(11));this.ticks.push([O,O])}this.max=this.ticks[ak-1][1];this.tickFactor=aw;this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor);if(!this.numberMinorTicks){this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor-1)}}else{if(q){var E=aL+(ak-1)*au;if(A>=E){A=E+au;ak+=1}else{A=E}this.tickInterval=this.tickInterval||au;this.numberTicks=this.numberTicks||ak;var O;for(aa=0;aa<this.numberTicks;aa++){O=parseFloat((aL+aa*this.tickInterval).toFixed(11));this.ticks.push([O,O])}this.max=this.ticks[this.numberTicks-1][1];this.tickFactor=aw;this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor);if(!this.numberMinorTicks){this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor-1)}}}if(!q&&!aG){var P=this.max-this.min;aw=Math.floor(parseFloat((Math.log(P)/Math.log(10)).toFixed(11)))-1;var aN=[5,6,4,7,3,8,9,10,2],V,C,av=0,M;if(P>1){var aJ=String(P);if(aJ.search(/\./)==-1){var aF=aJ.search(/0+$/);av=(aF>0)?aJ.length-aF-1:0}}M=P/Math.pow(10,av);for(aa=0;aa<aN.length;aa++){V=M/(aN[aa]-1);if(V==parseInt(V,10)){this.numberTicks=aN[aa];this.tickInterval=P/(this.numberTicks-1);this.tickFactor=aw+1;break}}var O;for(aa=0;aa<this.numberTicks;aa++){O=parseFloat((this.min+aa*this.tickInterval).toFixed(11));this.ticks.push([O,O])}this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor);if(!this.numberMinorTicks){this.numberMinorTicks=b(this.tickPositions,this.tickInterval,this.tickFactor-1)}if(!this.numberMinorTicks){this.numberMinorTicks=1;var aH=[4,5,3,6,2];for(aa=0;aa<5;aa++){var ao=this.tickInterval/aH[aa];if(ao==parseInt(ao,10)){this.numberMinorTicks=aH[aa]-1;break}}}}}var U=this._radius,aE=this.startAngle,k=this.endAngle,H=Math.PI,e=Math.PI/2;if(this.semiCircular){var z=Math.atan(this.innerPad/U),ac=this.outerStartAngle=aE-z,aB=this.outerEndAngle=k+z,B=this.hubStartAngle=aE-Math.atan(this.innerPad/this.hubRadius*2),af=this.hubEndAngle=k+Math.atan(this.innerPad/this.hubRadius*2);X.save();X.translate(this._center[0],this._center[1]);X.lineJoin="round";X.lineCap="round";X.save();X.beginPath();X.fillStyle=this.background;X.arc(0,0,U,ac,aB,false);X.closePath();X.fill();X.restore();var aj="rgba(0,0,0,"+this.shadowAlpha+")";X.save();for(var aa=0;aa<this.shadowDepth;aa++){X.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI),this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));X.beginPath();X.strokeStyle=aj;X.lineWidth=this.shadowWidth;X.arc(0,0,U,ac,aB,false);X.closePath();X.stroke()}X.restore();X.save();var az=parseInt((this.shadowDepth+1)/2,10);for(var aa=0;aa<az;aa++){X.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI),this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));X.beginPath();X.fillStyle=aj;X.arc(0,0,this.hubRadius,B,af,false);X.closePath();X.fill()}X.restore();X.save();X.beginPath();X.strokeStyle=this.ringColor;X.lineWidth=this.ringWidth;X.arc(0,0,U,ac,aB,false);X.closePath();X.stroke();X.restore();X.save();X.beginPath();X.fillStyle=this.ringColor;X.arc(0,0,this.hubRadius,B,af,false);X.closePath();X.fill();X.restore();if(this.showTicks){X.save();var f=this.tickOuterRadius,aq=this.tickLength,v=aq/2,F=this.numberMinorTicks,am=this.span*Math.PI/180/(this.ticks.length-1),p=am/(F+1);for(aa=0;aa<this.ticks.length;aa++){X.beginPath();X.lineWidth=1.5+this.diameter/360;X.strokeStyle=this.ringColor;var ae=am*aa+aE;X.moveTo(-f*Math.cos(am*aa+aE),f*Math.sin(am*aa+aE));X.lineTo(-(f-aq)*Math.cos(am*aa+aE),(f-aq)*Math.sin(am*aa+aE));this._tickPoints.push([(f-aq)*Math.cos(am*aa+aE)+this._center[0]+this.canvas._offsets.left,(f-aq)*Math.sin(am*aa+aE)+this._center[1]+this.canvas._offsets.top,am*aa+aE]);X.stroke();X.lineWidth=1+this.diameter/440;if(aa<this.ticks.length-1){for(var Y=1;Y<=F;Y++){X.beginPath();X.moveTo(-f*Math.cos(am*aa+p*Y+aE),f*Math.sin(am*aa+p*Y+aE));X.lineTo(-(f-v)*Math.cos(am*aa+p*Y+aE),(f-v)*Math.sin(am*aa+p*Y+aE));X.stroke()}}}X.restore()}if(this.showTickLabels){var J,W,T,aO,g,G,n=0;var an=this.tickPadding*(1-1/(this.diameter/80+1));for(aa=0;aa<this.ticks.length;aa++){J=c('<div class="jqplot-meterGauge-tick" style="position:absolute;">'+this.ticks[aa][1]+"</div>");this.canvas._elem.after(J);aO=J.outerWidth(true);g=J.outerHeight(true);W=this._tickPoints[aa][0]-aO*(this._tickPoints[aa][2]-Math.PI)/Math.PI-an*Math.cos(this._tickPoints[aa][2]);T=this._tickPoints[aa][1]-g/2+g/2*Math.pow(Math.abs((Math.sin(this._tickPoints[aa][2]))),0.5)+an/3*Math.pow(Math.abs((Math.sin(this._tickPoints[aa][2]))),0.5);J.css({left:W,top:T,color:this.tickColor});G=aO*Math.cos(this._tickPoints[aa][2])+g*Math.sin(Math.PI/2+this._tickPoints[aa][2]/2);n=(G>n)?G:n}}if(this.label&&this.labelPosition=="inside"){var W=this._center[0]+this.canvas._offsets.left;var an=this.tickPadding*(1-1/(this.diameter/80+1));var T=0.5*(this._center[1]+this.canvas._offsets.top-this.hubRadius)+0.5*(this._center[1]+this.canvas._offsets.top-this.tickOuterRadius+this.tickLength+an)+this.labelHeightAdjust;W-=this._labelElem.outerWidth(true)/2;T-=this._labelElem.outerHeight(true)/2;this._labelElem.css({left:W,top:T})}else{if(this.label&&this.labelPosition=="bottom"){var W=this._center[0]+this.canvas._offsets.left-this._labelElem.outerWidth(true)/2;var T=this._center[1]+this.canvas._offsets.top+this.innerPad+this.ringWidth+this.padding+this.labelHeightAdjust;this._labelElem.css({left:W,top:T})}}X.save();var ax=this.intervalInnerRadius||this.hubRadius*1.5;if(this.intervalOuterRadius==null){if(this.showTickLabels){var ag=(this.tickOuterRadius-this.tickLength-this.tickPadding-this.diameter/8)}else{var ag=(this.tickOuterRadius-this.tickLength-this.diameter/16)}}else{var ag=this.intervalOuterRadius}var P=this.max-this.min;var aD=this.intervals[this.intervals.length-1]-this.min;var y,Z,u=this.span*Math.PI/180;for(aa=0;aa<this.intervals.length;aa++){y=(aa==0)?aE:aE+(this.intervals[aa-1][0]-this.min)*u/P;if(y<0){y=0}Z=aE+(this.intervals[aa][0]-this.min)*u/P;if(Z<0){Z=0}X.beginPath();X.fillStyle=this.intervals[aa][2];X.arc(0,0,ax,y,Z,false);X.lineTo(ag*Math.cos(Z),ag*Math.sin(Z));X.arc(0,0,ag,Z,y,true);X.lineTo(ax*Math.cos(y),ax*Math.sin(y));X.closePath();X.fill()}X.restore();var ay=this.data[0][1];var R=this.max-this.min;if(this.pegNeedle){if(this.data[0][1]>this.max+R*3/this.span){ay=this.max+R*3/this.span}if(this.data[0][1]<this.min-R*3/this.span){ay=this.min-R*3/this.span}}var al=(ay-this.min)/R*this.span*Math.PI/180+this.startAngle;X.save();X.beginPath();X.fillStyle=this.ringColor;X.strokeStyle=this.ringColor;this.needleLength=(this.tickOuterRadius-this.tickLength)*0.85;this.needleThickness=(this.needleThickness<2)?2:this.needleThickness;var aK=this.needleThickness*0.4;var x=this.needleLength/10;var s=(this.needleThickness-aK)/10;var ar;for(var aa=0;aa<10;aa++){ar=this.needleThickness-aa*s;X.moveTo(x*aa*Math.cos(al),x*aa*Math.sin(al));X.lineWidth=ar;X.lineTo(x*(aa+1)*Math.cos(al),x*(aa+1)*Math.sin(al));X.stroke()}X.restore()}else{this._center=[(K-at*ai)/2+at*ai,(S-at*ah)/2+at*ah]}};c.jqplot.MeterGaugeAxisRenderer=function(){c.jqplot.LinearAxisRenderer.call(this)};c.jqplot.MeterGaugeAxisRenderer.prototype=new c.jqplot.LinearAxisRenderer();c.jqplot.MeterGaugeAxisRenderer.prototype.constructor=c.jqplot.MeterGaugeAxisRenderer;c.jqplot.MeterGaugeAxisRenderer.prototype.init=function(e){this.tickRenderer=c.jqplot.MeterGaugeTickRenderer;c.extend(true,this,e);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};c.jqplot.MeterGaugeLegendRenderer=function(){c.jqplot.TableLegendRenderer.call(this)};c.jqplot.MeterGaugeLegendRenderer.prototype=new c.jqplot.TableLegendRenderer();c.jqplot.MeterGaugeLegendRenderer.prototype.constructor=c.jqplot.MeterGaugeLegendRenderer;c.jqplot.MeterGaugeLegendRenderer.prototype.init=function(e){this.numberRows=null;this.numberColumns=null;c.extend(true,this,e)};c.jqplot.MeterGaugeLegendRenderer.prototype.draw=function(){if(this.show){var p=this._series;var x="position:absolute;";x+=(this.background)?"background:"+this.background+";":"";x+=(this.border)?"border:"+this.border+";":"";x+=(this.fontSize)?"font-size:"+this.fontSize+";":"";x+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";x+=(this.textColor)?"color:"+this.textColor+";":"";x+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";x+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";x+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";x+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=c('<table class="jqplot-table-legend" style="'+x+'"></table>');var f=false,q=false,u,o;var w=p[0];if(w.show){var t=w.data;if(this.numberRows){u=this.numberRows;if(!this.numberColumns){o=Math.ceil(t.length/u)}else{o=this.numberColumns}}else{if(this.numberColumns){o=this.numberColumns;u=Math.ceil(t.length/this.numberColumns)}else{u=t.length;o=1}}var n,m,r,g,e,l,k,h;var v=0;for(n=0;n<u;n++){if(q){r=c('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem)}else{r=c('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem)}for(m=0;m<o;m++){if(v<t.length){l=this.labels[v]||t[v][0].toString();h=w.color;if(!q){if(n>0){f=true}else{f=false}}else{if(n==u-1){f=false}else{f=true}}k=(f)?this.rowSpacing:"0";g=c('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+k+';"><div><div class="jqplot-table-legend-swatch" style="border-color:'+h+';"></div></div></td>');e=c('<td class="jqplot-table-legend" style="padding-top:'+k+';"></td>');if(this.escapeHtml){e.text(l)}else{e.html(l)}if(q){e.prependTo(r);g.prependTo(r)}else{g.appendTo(r);e.appendTo(r)}f=true}v++}}}}return this._elem};function a(j,h,f){f=f||{};f.axesDefaults=f.axesDefaults||{};f.legend=f.legend||{};f.seriesDefaults=f.seriesDefaults||{};f.grid=f.grid||{};var e=false;if(f.seriesDefaults.renderer==c.jqplot.MeterGaugeRenderer){e=true}else{if(f.series){for(var g=0;g<f.series.length;g++){if(f.series[g].renderer==c.jqplot.MeterGaugeRenderer){e=true}}}}if(e){f.axesDefaults.renderer=c.jqplot.MeterGaugeAxisRenderer;f.legend.renderer=c.jqplot.MeterGaugeLegendRenderer;f.legend.preDraw=true;f.grid.background=f.grid.background||"white";f.grid.drawGridlines=false;f.grid.borderWidth=(f.grid.borderWidth!=null)?f.grid.borderWidth:0;f.grid.shadow=(f.grid.shadow!=null)?f.grid.shadow:false}}function d(e){}c.jqplot.preInitHooks.push(a);c.jqplot.postParseOptionsHooks.push(d);c.jqplot.MeterGaugeTickRenderer=function(){c.jqplot.AxisTickRenderer.call(this)};c.jqplot.MeterGaugeTickRenderer.prototype=new c.jqplot.AxisTickRenderer();c.jqplot.MeterGaugeTickRenderer.prototype.constructor=c.jqplot.MeterGaugeTickRenderer})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.mobile.js b/web/extensions/jqplot/plugins/jqplot.mobile.js new file mode 100644 index 0000000..7fd60f1 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mobile.js @@ -0,0 +1,45 @@ +/** + * jqplot.jquerymobile plugin + * jQuery Mobile virtual event support. + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2011 Takashi Okamoto + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + */ +(function($) { + function postInit(target, data, options){ + this.bindCustomEvents = function() { + this.eventCanvas._elem.bind('vclick', {plot:this}, this.onClick); + this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); + this.eventCanvas._elem.bind('taphold', {plot:this}, this.onDblClick); + this.eventCanvas._elem.bind('vmousedown', {plot:this}, this.onMouseDown); + this.eventCanvas._elem.bind('vmousemove', {plot:this}, this.onMouseMove); + this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); + if (this.captureRightClick) { + this.eventCanvas._elem.bind('vmouseup', {plot:this}, this.onRightClick); + this.eventCanvas._elem.get(0).oncontextmenu = function() { + return false; + }; + } + else { + this.eventCanvas._elem.bind('vmouseup', {plot:this}, this.onMouseUp); + } + }; + this.plugins.mobile = true; + } + $.jqplot.postInitHooks.push(postInit); +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.mobile.min.js b/web/extensions/jqplot/plugins/jqplot.mobile.min.js new file mode 100644 index 0000000..c968173 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.mobile.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(b){function a(e,d,c){this.bindCustomEvents=function(){this.eventCanvas._elem.bind("vclick",{plot:this},this.onClick);this.eventCanvas._elem.bind("dblclick",{plot:this},this.onDblClick);this.eventCanvas._elem.bind("taphold",{plot:this},this.onDblClick);this.eventCanvas._elem.bind("vmousedown",{plot:this},this.onMouseDown);this.eventCanvas._elem.bind("vmousemove",{plot:this},this.onMouseMove);this.eventCanvas._elem.bind("mouseenter",{plot:this},this.onMouseEnter);this.eventCanvas._elem.bind("mouseleave",{plot:this},this.onMouseLeave);if(this.captureRightClick){this.eventCanvas._elem.bind("vmouseup",{plot:this},this.onRightClick);this.eventCanvas._elem.get(0).oncontextmenu=function(){return false}}else{this.eventCanvas._elem.bind("vmouseup",{plot:this},this.onMouseUp)}};this.plugins.mobile=true}b.jqplot.postInitHooks.push(a)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.js b/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.js new file mode 100644 index 0000000..3cffbfb --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.js @@ -0,0 +1,373 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.OHLCRenderer + * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts. + * + * To use this plugin, include the renderer js file in + * your source: + * + * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script> + * + * You will most likely want to use a date axis renderer + * for the x axis also, so include the date axis render js file also: + * + * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script> + * + * Then you set the renderer in the series options on your plot: + * + * > series: [{renderer:$.jqplot.OHLCRenderer}] + * + * For OHLC and candlestick charts, data should be specified + * like so: + * + * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...] + * + * If the data array has only 4 values per point instead of 5, + * the renderer will create a Hi Low Close chart instead. In that case, + * data should be supplied like: + * + * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...] + * + * To generate a candlestick chart instead of an OHLC chart, + * set the "candlestick" option to true: + * + * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}], + * + */ + $.jqplot.OHLCRenderer = function(){ + // subclass line renderer to make use of some of its methods. + $.jqplot.LineRenderer.call(this); + // prop: candleStick + // true to render chart as candleStick. + // Must have an open price, cannot be a hlc chart. + this.candleStick = false; + // prop: tickLength + // length of the line in pixels indicating open and close price. + // Default will auto calculate based on plot width and + // number of points displayed. + this.tickLength = 'auto'; + // prop: bodyWidth + // width of the candlestick body in pixels. Default will auto calculate + // based on plot width and number of candlesticks displayed. + this.bodyWidth = 'auto'; + // prop: openColor + // color of the open price tick mark. Default is series color. + this.openColor = null; + // prop: closeColor + // color of the close price tick mark. Default is series color. + this.closeColor = null; + // prop: wickColor + // color of the hi-lo line thorugh the candlestick body. + // Default is the series color. + this.wickColor = null; + // prop: fillUpBody + // true to render an "up" day (close price greater than open price) + // with a filled candlestick body. + this.fillUpBody = false; + // prop: fillDownBody + // true to render a "down" day (close price lower than open price) + // with a filled candlestick body. + this.fillDownBody = true; + // prop: upBodyColor + // Color of candlestick body of an "up" day. Default is series color. + this.upBodyColor = null; + // prop: downBodyColor + // Color of candlestick body on a "down" day. Default is series color. + this.downBodyColor = null; + // prop: hlc + // true if is a hi-low-close chart (no open price). + // This is determined automatically from the series data. + this.hlc = false; + // prop: lineWidth + // Width of the hi-low line and open/close ticks. + // Must be set in the rendererOptions for the series. + this.lineWidth = 1.5; + this._tickLength; + this._bodyWidth; + }; + + $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer; + + // called with scope of series. + $.jqplot.OHLCRenderer.prototype.init = function(options) { + options = options || {}; + // lineWidth has to be set on the series, changes in renderer + // constructor have no effect. set the default here + // if no renderer option for lineWidth is specified. + this.lineWidth = options.lineWidth || 1.5; + $.jqplot.LineRenderer.prototype.init.call(this, options); + this._type = 'ohlc'; + // set the yaxis data bounds here to account for hi and low values + var db = this._yaxis._dataBounds; + var d = this._plotData; + // if data points have less than 5 values, force a hlc chart. + if (d[0].length < 5) { + this.renderer.hlc = true; + + for (var j=0; j<d.length; j++) { + if (d[j][2] < db.min || db.min == null) { + db.min = d[j][2]; + } + if (d[j][1] > db.max || db.max == null) { + db.max = d[j][1]; + } + } + } + else { + for (var j=0; j<d.length; j++) { + if (d[j][3] < db.min || db.min == null) { + db.min = d[j][3]; + } + if (d[j][2] > db.max || db.max == null) { + db.max = d[j][2]; + } + } + } + + }; + + // called within scope of series. + $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) { + var d = this.data; + var xmin = this._xaxis.min; + var xmax = this._xaxis.max; + // index of last value below range of plot. + var xminidx = 0; + // index of first value above range of plot. + var xmaxidx = d.length; + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var i, prevColor, ops, b, h, w, a, points; + var o; + var r = this.renderer; + var opts = (options != undefined) ? options : {}; + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; + r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth; + r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength; + ctx.save(); + if (this.show) { + var x, open, hi, low, close; + // need to get widths based on number of points shown, + // not on total number of points. Use the results + // to speed up drawing in next step. + for (var i=0; i<d.length; i++) { + if (d[i][0] < xmin) { + xminidx = i; + } + else if (d[i][0] < xmax) { + xmaxidx = i+1; + } + } + + var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0]; + var nvisiblePoints = xmaxidx - xminidx; + try { + var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval, 10)) - this._xaxis.series_u2p(0)); + } + + catch (e) { + var dinterval = dwidth / nvisiblePoints; + } + + if (r.candleStick) { + if (typeof(r.bodyWidth) == 'number') { + r._bodyWidth = r.bodyWidth; + } + else { + r._bodyWidth = Math.min(20, dinterval/1.65); + } + } + else { + if (typeof(r.tickLength) == 'number') { + r._tickLength = r.tickLength; + } + else { + r._tickLength = Math.min(10, dinterval/3.5); + } + } + + for (var i=xminidx; i<xmaxidx; i++) { + x = xp(d[i][0]); + if (r.hlc) { + open = null; + hi = yp(d[i][1]); + low = yp(d[i][2]); + close = yp(d[i][3]); + } + else { + open = yp(d[i][1]); + hi = yp(d[i][2]); + low = yp(d[i][3]); + close = yp(d[i][4]); + } + o = {}; + if (r.candleStick && !r.hlc) { + w = r._bodyWidth; + a = x - w/2; + // draw candle + // determine if candle up or down + // up, remember grid coordinates increase downward + if (close < open) { + // draw wick + if (r.wickColor) { + o.color = r.wickColor; + } + else if (r.downBodyColor) { + o.color = r.upBodyColor; + } + ops = $.extend(true, {}, opts, o); + r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops); + r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops); + o = {}; + b = close; + h = open - close; + // if color specified, use it + if (r.fillUpBody) { + o.fillRect = true; + } + else { + o.strokeRect = true; + w = w - this.lineWidth; + a = x - w/2; + } + if (r.upBodyColor) { + o.color = r.upBodyColor; + o.fillStyle = r.upBodyColor; + } + points = [a, b, w, h]; + } + // down + else if (close > open) { + // draw wick + if (r.wickColor) { + o.color = r.wickColor; + } + else if (r.downBodyColor) { + o.color = r.downBodyColor; + } + ops = $.extend(true, {}, opts, o); + r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops); + r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops); + + o = {}; + + b = open; + h = close - open; + // if color specified, use it + if (r.fillDownBody) { + o.fillRect = true; + } + else { + o.strokeRect = true; + w = w - this.lineWidth; + a = x - w/2; + } + if (r.downBodyColor) { + o.color = r.downBodyColor; + o.fillStyle = r.downBodyColor; + } + points = [a, b, w, h]; + } + // even, open = close + else { + // draw wick + if (r.wickColor) { + o.color = r.wickColor; + } + ops = $.extend(true, {}, opts, o); + r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops); + o = {}; + o.fillRect = false; + o.strokeRect = false; + a = [x - w/2, open]; + b = [x + w/2, close]; + w = null; + h = null; + points = [a, b]; + } + ops = $.extend(true, {}, opts, o); + r.shapeRenderer.draw(ctx, points, ops); + } + else { + prevColor = opts.color; + if (r.openColor) { + opts.color = r.openColor; + } + // draw open tick + if (!r.hlc) { + r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts); + } + opts.color = prevColor; + // draw wick + if (r.wickColor) { + opts.color = r.wickColor; + } + r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts); + opts.color = prevColor; + // draw close tick + if (r.closeColor) { + opts.color = r.closeColor; + } + r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts); + opts.color = prevColor; + } + } + } + + ctx.restore(); + }; + + $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) { + // This is a no-op, shadows drawn with lines. + }; + + // called with scope of plot. + $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) { + // provide some sensible highlighter options by default + // These aren't good for hlc, only for ohlc or candlestick + if (!options.highlighter) { + options.highlighter = { + showMarker:false, + tooltipAxes: 'y', + yvalues: 4, + formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>' + }; + } + }; + + //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions); + +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.min.js new file mode 100644 index 0000000..540bcf5 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.ohlcRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.OHLCRenderer=function(){a.jqplot.LineRenderer.call(this);this.candleStick=false;this.tickLength="auto";this.bodyWidth="auto";this.openColor=null;this.closeColor=null;this.wickColor=null;this.fillUpBody=false;this.fillDownBody=true;this.upBodyColor=null;this.downBodyColor=null;this.hlc=false;this.lineWidth=1.5;this._tickLength;this._bodyWidth};a.jqplot.OHLCRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.OHLCRenderer.prototype.constructor=a.jqplot.OHLCRenderer;a.jqplot.OHLCRenderer.prototype.init=function(e){e=e||{};this.lineWidth=e.lineWidth||1.5;a.jqplot.LineRenderer.prototype.init.call(this,e);this._type="ohlc";var b=this._yaxis._dataBounds;var f=this._plotData;if(f[0].length<5){this.renderer.hlc=true;for(var c=0;c<f.length;c++){if(f[c][2]<b.min||b.min==null){b.min=f[c][2]}if(f[c][1]>b.max||b.max==null){b.max=f[c][1]}}}else{for(var c=0;c<f.length;c++){if(f[c][3]<b.min||b.min==null){b.min=f[c][3]}if(f[c][2]>b.max||b.max==null){b.max=f[c][2]}}}};a.jqplot.OHLCRenderer.prototype.draw=function(A,N,j){var J=this.data;var v=this._xaxis.min;var z=this._xaxis.max;var l=0;var K=J.length;var p=this._xaxis.series_u2p;var G=this._yaxis.series_u2p;var D,E,f,M,F,n,O,C;var y;var u=this.renderer;var s=(j!=undefined)?j:{};var k=(s.shadow!=undefined)?s.shadow:this.shadow;var B=(s.fill!=undefined)?s.fill:this.fill;var c=(s.fillAndStroke!=undefined)?s.fillAndStroke:this.fillAndStroke;u.bodyWidth=(s.bodyWidth!=undefined)?s.bodyWidth:u.bodyWidth;u.tickLength=(s.tickLength!=undefined)?s.tickLength:u.tickLength;A.save();if(this.show){var m,q,g,Q,t;for(var D=0;D<J.length;D++){if(J[D][0]<v){l=D}else{if(J[D][0]<z){K=D+1}}}var I=this.gridData[K-1][0]-this.gridData[l][0];var L=K-l;try{var P=Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval,10))-this._xaxis.series_u2p(0))}catch(H){var P=I/L}if(u.candleStick){if(typeof(u.bodyWidth)=="number"){u._bodyWidth=u.bodyWidth}else{u._bodyWidth=Math.min(20,P/1.65)}}else{if(typeof(u.tickLength)=="number"){u._tickLength=u.tickLength}else{u._tickLength=Math.min(10,P/3.5)}}for(var D=l;D<K;D++){m=p(J[D][0]);if(u.hlc){q=null;g=G(J[D][1]);Q=G(J[D][2]);t=G(J[D][3])}else{q=G(J[D][1]);g=G(J[D][2]);Q=G(J[D][3]);t=G(J[D][4])}y={};if(u.candleStick&&!u.hlc){n=u._bodyWidth;O=m-n/2;if(t<q){if(u.wickColor){y.color=u.wickColor}else{if(u.downBodyColor){y.color=u.upBodyColor}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,t]],f);u.shapeRenderer.draw(A,[[m,q],[m,Q]],f);y={};M=t;F=q-t;if(u.fillUpBody){y.fillRect=true}else{y.strokeRect=true;n=n-this.lineWidth;O=m-n/2}if(u.upBodyColor){y.color=u.upBodyColor;y.fillStyle=u.upBodyColor}C=[O,M,n,F]}else{if(t>q){if(u.wickColor){y.color=u.wickColor}else{if(u.downBodyColor){y.color=u.downBodyColor}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,q]],f);u.shapeRenderer.draw(A,[[m,t],[m,Q]],f);y={};M=q;F=t-q;if(u.fillDownBody){y.fillRect=true}else{y.strokeRect=true;n=n-this.lineWidth;O=m-n/2}if(u.downBodyColor){y.color=u.downBodyColor;y.fillStyle=u.downBodyColor}C=[O,M,n,F]}else{if(u.wickColor){y.color=u.wickColor}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,Q]],f);y={};y.fillRect=false;y.strokeRect=false;O=[m-n/2,q];M=[m+n/2,t];n=null;F=null;C=[O,M]}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,C,f)}else{E=s.color;if(u.openColor){s.color=u.openColor}if(!u.hlc){u.shapeRenderer.draw(A,[[m-u._tickLength,q],[m,q]],s)}s.color=E;if(u.wickColor){s.color=u.wickColor}u.shapeRenderer.draw(A,[[m,g],[m,Q]],s);s.color=E;if(u.closeColor){s.color=u.closeColor}u.shapeRenderer.draw(A,[[m,t],[m+u._tickLength,t]],s);s.color=E}}}A.restore()};a.jqplot.OHLCRenderer.prototype.drawShadow=function(b,d,c){};a.jqplot.OHLCRenderer.checkOptions=function(d,c,b){if(!b.highlighter){b.highlighter={showMarker:false,tooltipAxes:"y",yvalues:4,formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'}}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pieRenderer.js b/web/extensions/jqplot/plugins/jqplot.pieRenderer.js new file mode 100644 index 0000000..1d508bd --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pieRenderer.js @@ -0,0 +1,904 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + /** + * Class: $.jqplot.PieRenderer + * Plugin renderer to draw a pie chart. + * x values, if present, will be used as slice labels. + * y values give slice size. + * + * To use this renderer, you need to include the + * pie renderer plugin, for example: + * + * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script> + * + * Properties described here are passed into the $.jqplot function + * as options on the series renderer. For example: + * + * > plot2 = $.jqplot('chart2', [s1, s2], { + * > seriesDefaults: { + * > renderer:$.jqplot.PieRenderer, + * > rendererOptions:{ + * > sliceMargin: 2, + * > startAngle: -90 + * > } + * > } + * > }); + * + * A pie plot will trigger events on the plot target + * according to user interaction. All events return the event object, + * the series index, the point (slice) index, and the point data for + * the appropriate slice. + * + * 'jqplotDataMouseOver' - triggered when user mouseing over a slice. + * 'jqplotDataHighlight' - triggered the first time user mouses over a slice, + * if highlighting is enabled. + * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of + * a highlighted slice. + * 'jqplotDataClick' - triggered when the user clicks on a slice. + * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if + * the "captureRightClick" option is set to true on the plot. + */ + $.jqplot.PieRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer; + + // called with scope of a series + $.jqplot.PieRenderer.prototype.init = function(options, plot) { + // Group: Properties + // + // prop: diameter + // Outer diameter of the pie, auto computed by default + this.diameter = null; + // prop: padding + // padding between the pie and plot edges, legend, etc. + this.padding = 20; + // prop: sliceMargin + // angular spacing between pie slices in degrees. + this.sliceMargin = 0; + // prop: fill + // true or false, whether to fil the slices. + this.fill = true; + // prop: shadowOffset + // offset of the shadow from the slice and offset of + // each succesive stroke of the shadow from the last. + this.shadowOffset = 2; + // prop: shadowAlpha + // transparency of the shadow (0 = transparent, 1 = opaque) + this.shadowAlpha = 0.07; + // prop: shadowDepth + // number of strokes to apply to the shadow, + // each stroke offset shadowOffset from the last. + this.shadowDepth = 5; + // prop: highlightMouseOver + // True to highlight slice when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a slice. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a slice. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // an array of colors to use when highlighting a slice. + this.highlightColors = []; + // prop: dataLabels + // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. + // Defaults to percentage of each pie slice. + this.dataLabels = 'percent'; + // prop: showDataLabels + // true to show data labels on slices. + this.showDataLabels = false; + // prop: dataLabelFormatString + // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. + this.dataLabelFormatString = null; + // prop: dataLabelThreshold + // Threshhold in percentage (0-100) of pie area, below which no label will be displayed. + // This applies to all label types, not just to percentage labels. + this.dataLabelThreshold = 3; + // prop: dataLabelPositionFactor + // A Multiplier (0-1) of the pie radius which controls position of label on slice. + // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. + this.dataLabelPositionFactor = 0.52; + // prop: dataLabelNudge + // Number of pixels to slide the label away from (+) or toward (-) the center of the pie. + this.dataLabelNudge = 2; + // prop: dataLabelCenterOn + // True to center the data label at its position. + // False to set the inside facing edge of the label at its position. + this.dataLabelCenterOn = true; + // prop: startAngle + // Angle to start drawing pie in degrees. + // According to orientation of canvas coordinate system: + // 0 = on the positive x axis + // -90 = on the positive y axis. + // 90 = on the negaive y axis. + // 180 or - 180 = on the negative x axis. + this.startAngle = 0; + this.tickRenderer = $.jqplot.PieTickRenderer; + // Used as check for conditions where pie shouldn't be drawn. + this._drawData = true; + this._type = 'pie'; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + $.extend(true, this, options); + + if (this.sliceMargin < 0) { + this.sliceMargin = 0; + } + + this._diameter = null; + this._radius = null; + // array of [start,end] angles arrays, one for each slice. In radians. + this._sliceAngles = []; + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + + // set highlight colors if none provided + if (this.highlightColors.length == 0) { + for (var i=0; i<this.seriesColors.length; i++){ + var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); + var newrgb = [rgba[0], rgba[1], rgba[2]]; + var sum = newrgb[0] + newrgb[1] + newrgb[2]; + for (var j=0; j<3; j++) { + // when darkening, lowest color component can be is 60. + newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); + } + } + + this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); + + plot.postParseOptionsHooks.addOnce(postParseOptions); + plot.postInitHooks.addOnce(postInit); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); + plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); + plot.eventListenerHooks.addOnce('jqplotClick', handleClick); + plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); + plot.postDrawHooks.addOnce(postPlotDraw); + }; + + $.jqplot.PieRenderer.prototype.setGridData = function(plot) { + // set gridData property. This will hold angle in radians of each data point. + var stack = []; + var td = []; + var sa = this.startAngle/180*Math.PI; + var tot = 0; + // don't know if we have any valid data yet, so set plot to not draw. + this._drawData = false; + for (var i=0; i<this.data.length; i++){ + if (this.data[i][1] != 0) { + // we have data, O.K. to draw. + this._drawData = true; + } + stack.push(this.data[i][1]); + td.push([this.data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + tot += this.data[i][1]; + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + td[i][2] = this.data[i][1]/tot; + } + this.gridData = td; + }; + + $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) { + var stack = []; + var td = []; + var tot = 0; + var sa = this.startAngle/180*Math.PI; + // don't know if we have any valid data yet, so set plot to not draw. + this._drawData = false; + for (var i=0; i<data.length; i++){ + if (this.data[i][1] != 0) { + // we have data, O.K. to draw. + this._drawData = true; + } + stack.push(data[i][1]); + td.push([data[i][0]]); + if (i>0) { + stack[i] += stack[i-1]; + } + tot += data[i][1]; + } + var fact = Math.PI*2/stack[stack.length - 1]; + + for (var i=0; i<stack.length; i++) { + td[i][1] = stack[i] * fact; + td[i][2] = data[i][1]/tot; + } + return td; + }; + + function calcRadiusAdjustment(ang) { + return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0); + } + + function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) { + var rprime = 0; + var ang = ang2 - ang1; + var absang = Math.abs(ang); + var sm = sliceMargin; + if (fill == false) { + sm += lineWidth; + } + + if (sm > 0 && absang > 0.01 && absang < 6.282) { + rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang); + } + + return rprime; + } + + $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { + if (this._drawData) { + var r = this._radius; + var fill = this.fill; + var lineWidth = this.lineWidth; + var sm = this.sliceMargin; + if (this.fill == false) { + sm += this.lineWidth; + } + ctx.save(); + ctx.translate(this._center[0], this._center[1]); + + var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); + + var transx = rprime * Math.cos((ang1 + ang2) / 2.0); + var transy = rprime * Math.sin((ang1 + ang2) / 2.0); + + if ((ang2 - ang1) <= Math.PI) { + r -= rprime; + } + else { + r += rprime; + } + + ctx.translate(transx, transy); + + if (isShadow) { + for (var i=0, l=this.shadowDepth; i<l; i++) { + ctx.save(); + ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); + doDraw(r); + } + for (var i=0, l=this.shadowDepth; i<l; i++) { + ctx.restore(); + } + } + + else { + doDraw(r); + } + ctx.restore(); + } + + function doDraw (rad) { + // Fix for IE and Chrome that can't seem to draw circles correctly. + // ang2 should always be <= 2 pi since that is the way the data is converted. + // 2Pi = 6.2831853, Pi = 3.1415927 + if (ang2 > 6.282 + this.startAngle) { + ang2 = 6.282 + this.startAngle; + if (ang1 > ang2) { + ang1 = 6.281 + this.startAngle; + } + } + // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids + // ugly line on unfilled pies. + if (ang1 >= ang2) { + return; + } + + ctx.beginPath(); + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + ctx.arc(0, 0, rad, ang1, ang2, false); + ctx.lineTo(0,0); + ctx.closePath(); + + if (fill) { + ctx.fill(); + } + else { + ctx.stroke(); + } + } + }; + + // called with scope of series + $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) { + var i; + var opts = (options != undefined) ? options : {}; + // offset and direction of offset due to legend placement + var offx = 0; + var offy = 0; + var trans = 1; + var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); + if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { + var li = options.legendInfo; + switch (li.location) { + case 'nw': + offx = li.width + li.xoffset; + break; + case 'w': + offx = li.width + li.xoffset; + break; + case 'sw': + offx = li.width + li.xoffset; + break; + case 'ne': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'e': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'se': + offx = li.width + li.xoffset; + trans = -1; + break; + case 'n': + offy = li.height + li.yoffset; + break; + case 's': + offy = li.height + li.yoffset; + trans = -1; + break; + default: + break; + } + } + + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var cw = ctx.canvas.width; + var ch = ctx.canvas.height; + var w = cw - offx - 2 * this.padding; + var h = ch - offy - 2 * this.padding; + var mindim = Math.min(w,h); + var d = mindim; + + // Fixes issue #272. Thanks hugwijst! + // reset slice angles array. + this._sliceAngles = []; + + var sm = this.sliceMargin; + if (this.fill == false) { + sm += this.lineWidth; + } + + var rprime; + var maxrprime = 0; + + var ang, ang1, ang2, shadowColor; + var sa = this.startAngle / 180 * Math.PI; + + // have to pre-draw shadows, so loop throgh here and calculate some values also. + for (var i=0, l=gd.length; i<l; i++) { + ang1 = (i == 0) ? sa : gd[i-1][1] + sa; + ang2 = gd[i][1] + sa; + + this._sliceAngles.push([ang1, ang2]); + + rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); + + if (Math.abs(ang2-ang1) > Math.PI) { + maxrprime = Math.max(rprime, maxrprime); + } + } + + if (this.diameter != null && this.diameter > 0) { + this._diameter = this.diameter - 2*maxrprime; + } + else { + this._diameter = d - 2*maxrprime; + } + + // Need to check for undersized pie. This can happen if + // plot area too small and legend is too big. + if (this._diameter < 6) { + $.jqplot.log('Diameter of pie too small, not rendering.'); + return; + } + + var r = this._radius = this._diameter/2; + + this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)]; + + if (this.shadow) { + for (var i=0, l=gd.length; i<l; i++) { + shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; + this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true); + } + } + + for (var i=0; i<gd.length; i++) { + + this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false); + + if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { + var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label; + + if (this.dataLabels == 'label') { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, gd[i][0]); + } + else if (this.dataLabels == 'value') { + fstr = this.dataLabelFormatString || '%d'; + label = $.jqplot.sprintf(fstr, this.data[i][1]); + } + else if (this.dataLabels == 'percent') { + fstr = this.dataLabelFormatString || '%d%%'; + label = $.jqplot.sprintf(fstr, gd[i][2]*100); + } + else if (this.dataLabels.constructor == Array) { + fstr = this.dataLabelFormatString || '%s'; + label = $.jqplot.sprintf(fstr, this.dataLabels[i]); + } + + var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; + + var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; + var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; + + var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem); + if (this.dataLabelCenterOn) { + x -= labelelem.width()/2; + y -= labelelem.height()/2; + } + else { + x -= labelelem.width() * Math.sin(avgang/2); + y -= labelelem.height()/2; + } + x = Math.round(x); + y = Math.round(y); + labelelem.css({left: x, top: y}); + } + } + }; + + $.jqplot.PieAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer; + + + // There are no traditional axes on a pie chart. We just need to provide + // dummy objects with properties so the plot will render. + // called with scope of axis object. + $.jqplot.PieAxisRenderer.prototype.init = function(options){ + // + this.tickRenderer = $.jqplot.PieTickRenderer; + $.extend(true, this, options); + // I don't think I'm going to need _dataBounds here. + // have to go Axis scaling in a way to fit chart onto plot area + // and provide u2p and p2u functionality for mouse cursor, etc. + // for convienence set _dataBounds to 0 and 100 and + // set min/max to 0 and 100. + this._dataBounds = {min:0, max:100}; + this.min = 0; + this.max = 100; + this.showTicks = false; + this.ticks = []; + this.showMark = false; + this.show = false; + }; + + + + + $.jqplot.PieLegendRenderer = function(){ + $.jqplot.TableLegendRenderer.call(this); + }; + + $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); + $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer; + + /** + * Class: $.jqplot.PieLegendRenderer + * Legend Renderer specific to pie plots. Set by default + * when user creates a pie plot. + */ + $.jqplot.PieLegendRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: numberRows + // Maximum number of rows in the legend. 0 or null for unlimited. + this.numberRows = null; + // prop: numberColumns + // Maximum number of columns in the legend. 0 or null for unlimited. + this.numberColumns = null; + $.extend(true, this, options); + }; + + // called with context of legend + $.jqplot.PieLegendRenderer.prototype.draw = function() { + var legend = this; + if (this.show) { + var series = this._series; + + + this._elem = $(document.createElement('table')); + this._elem.addClass('jqplot-table-legend'); + + var ss = {position:'absolute'}; + if (this.background) { + ss['background'] = this.background; + } + if (this.border) { + ss['border'] = this.border; + } + if (this.fontSize) { + ss['fontSize'] = this.fontSize; + } + if (this.fontFamily) { + ss['fontFamily'] = this.fontFamily; + } + if (this.textColor) { + ss['textColor'] = this.textColor; + } + if (this.marginTop != null) { + ss['marginTop'] = this.marginTop; + } + if (this.marginBottom != null) { + ss['marginBottom'] = this.marginBottom; + } + if (this.marginLeft != null) { + ss['marginLeft'] = this.marginLeft; + } + if (this.marginRight != null) { + ss['marginRight'] = this.marginRight; + } + + this._elem.css(ss); + + // Pie charts legends don't go by number of series, but by number of data points + // in the series. Refactor things here for that. + + var pad = false, + reverse = false, + nr, + nc; + var s = series[0]; + var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); + + if (s.show) { + var pd = s.data; + if (this.numberRows) { + nr = this.numberRows; + if (!this.numberColumns){ + nc = Math.ceil(pd.length/nr); + } + else{ + nc = this.numberColumns; + } + } + else if (this.numberColumns) { + nc = this.numberColumns; + nr = Math.ceil(pd.length/this.numberColumns); + } + else { + nr = pd.length; + nc = 1; + } + + var i, j; + var tr, td1, td2; + var lt, rs, color; + var idx = 0; + var div0, div1; + + for (i=0; i<nr; i++) { + tr = $(document.createElement('tr')); + tr.addClass('jqplot-table-legend'); + + if (reverse){ + tr.prependTo(this._elem); + } + + else{ + tr.appendTo(this._elem); + } + + for (j=0; j<nc; j++) { + if (idx < pd.length){ + lt = this.labels[idx] || pd[idx][0].toString(); + color = colorGenerator.next(); + if (!reverse){ + if (i>0){ + pad = true; + } + else{ + pad = false; + } + } + else{ + if (i == nr -1){ + pad = false; + } + else{ + pad = true; + } + } + rs = (pad) ? this.rowSpacing : '0'; + + + + td1 = $(document.createElement('td')); + td1.addClass('jqplot-table-legend jqplot-table-legend-swatch'); + td1.css({textAlign: 'center', paddingTop: rs}); + + div0 = $(document.createElement('div')); + div0.addClass('jqplot-table-legend-swatch-outline'); + div1 = $(document.createElement('div')); + div1.addClass('jqplot-table-legend-swatch'); + div1.css({backgroundColor: color, borderColor: color}); + td1.append(div0.append(div1)); + + td2 = $(document.createElement('td')); + td2.addClass('jqplot-table-legend jqplot-table-legend-label'); + td2.css('paddingTop', rs); + + if (this.escapeHtml){ + td2.text(lt); + } + else { + td2.html(lt); + } + if (reverse) { + td2.prependTo(tr); + td1.prependTo(tr); + } + else { + td1.appendTo(tr); + td2.appendTo(tr); + } + pad = true; + } + idx++; + } + } + } + } + return this._elem; + }; + + $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + plot.target.trigger('jqplotDataMouseOver', ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + plot.target.trigger('jqplotDataHighlight', ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + }; + + + // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + // only set these if there is a pie series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.PieRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.PieAxisRenderer; + options.legend.renderer = $.jqplot.PieLegendRenderer; + options.legend.preDraw = true; + options.seriesDefaults.pointLabels = {show: false}; + } + } + + function postInit(target, data, options) { + for (var i=0; i<this.series.length; i++) { + if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) { + // don't allow mouseover and mousedown at same time. + if (this.series[i].highlightMouseOver) { + this.series[i].highlightMouseDown = false; + } + } + } + } + + // called with scope of plot + function postParseOptions(options) { + for (var i=0; i<this.series.length; i++) { + this.series[i].seriesColors = this.seriesColors; + this.series[i].colorGenerator = $.jqplot.colorGenerator; + } + } + + function highlight (plot, sidx, pidx) { + var s = plot.series[sidx]; + var canvas = plot.plugins.pieRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.pieRenderer.highlightedSeriesIndex = sidx; + s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false); + } + + function unhighlight (plot) { + var canvas = plot.plugins.pieRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.pieRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + } + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, ins[0], ins[1]); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { + var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + } + + function handleClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt = jQuery.Event('jqplotDataClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + function handleRightClick(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; + if (idx != null && plot.series[idx].highlightMouseDown) { + unhighlight(plot); + } + var evt = jQuery.Event('jqplotDataRightClick'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) { + this.plugins.pieRenderer.highlightCanvas.resetCanvas(); + this.plugins.pieRenderer.highlightCanvas = null; + } + + this.plugins.pieRenderer = {highlightedSeriesIndex:null}; + this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + + // do we have any data labels? if so, put highlight canvas before those + var labels = $(this.targetId+' .jqplot-data-label'); + if (labels.length) { + $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); + } + // else put highlight canvas before event canvas. + else { + this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); + } + + var hctx = this.plugins.pieRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + $.jqplot.preInitHooks.push(preInit); + + $.jqplot.PieTickRenderer = function() { + $.jqplot.AxisTickRenderer.call(this); + }; + + $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); + $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer; + +})(jQuery); + + \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pieRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.pieRenderer.min.js new file mode 100644 index 0000000..a09f8f1 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pieRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(e){e.jqplot.PieRenderer=function(){e.jqplot.LineRenderer.call(this)};e.jqplot.PieRenderer.prototype=new e.jqplot.LineRenderer();e.jqplot.PieRenderer.prototype.constructor=e.jqplot.PieRenderer;e.jqplot.PieRenderer.prototype.init=function(q,u){this.diameter=null;this.padding=20;this.sliceMargin=0;this.fill=true;this.shadowOffset=2;this.shadowAlpha=0.07;this.shadowDepth=5;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.dataLabels="percent";this.showDataLabels=false;this.dataLabelFormatString=null;this.dataLabelThreshold=3;this.dataLabelPositionFactor=0.52;this.dataLabelNudge=2;this.dataLabelCenterOn=true;this.startAngle=0;this.tickRenderer=e.jqplot.PieTickRenderer;this._drawData=true;this._type="pie";if(q.highlightMouseDown&&q.highlightMouseOver==null){q.highlightMouseOver=false}e.extend(true,this,q);if(this.sliceMargin<0){this.sliceMargin=0}this._diameter=null;this._radius=null;this._sliceAngles=[];this._highlightedPoint=null;if(this.highlightColors.length==0){for(var s=0;s<this.seriesColors.length;s++){var r=e.jqplot.getColorComponents(this.seriesColors[s]);var o=[r[0],r[1],r[2]];var t=o[0]+o[1]+o[2];for(var p=0;p<3;p++){o[p]=(t>570)?o[p]*0.8:o[p]+0.3*(255-o[p]);o[p]=parseInt(o[p],10)}this.highlightColors.push("rgb("+o[0]+","+o[1]+","+o[2]+")")}}this.highlightColorGenerator=new e.jqplot.ColorGenerator(this.highlightColors);u.postParseOptionsHooks.addOnce(m);u.postInitHooks.addOnce(g);u.eventListenerHooks.addOnce("jqplotMouseMove",b);u.eventListenerHooks.addOnce("jqplotMouseDown",a);u.eventListenerHooks.addOnce("jqplotMouseUp",l);u.eventListenerHooks.addOnce("jqplotClick",f);u.eventListenerHooks.addOnce("jqplotRightClick",n);u.postDrawHooks.addOnce(i)};e.jqplot.PieRenderer.prototype.setGridData=function(t){var p=[];var u=[];var o=this.startAngle/180*Math.PI;var s=0;this._drawData=false;for(var r=0;r<this.data.length;r++){if(this.data[r][1]!=0){this._drawData=true}p.push(this.data[r][1]);u.push([this.data[r][0]]);if(r>0){p[r]+=p[r-1]}s+=this.data[r][1]}var q=Math.PI*2/p[p.length-1];for(var r=0;r<p.length;r++){u[r][1]=p[r]*q;u[r][2]=this.data[r][1]/s}this.gridData=u};e.jqplot.PieRenderer.prototype.makeGridData=function(t,u){var p=[];var v=[];var s=0;var o=this.startAngle/180*Math.PI;this._drawData=false;for(var r=0;r<t.length;r++){if(this.data[r][1]!=0){this._drawData=true}p.push(t[r][1]);v.push([t[r][0]]);if(r>0){p[r]+=p[r-1]}s+=t[r][1]}var q=Math.PI*2/p[p.length-1];for(var r=0;r<p.length;r++){v[r][1]=p[r]*q;v[r][2]=t[r][1]/s}return v};function h(o){return Math.sin((o-(o-Math.PI)/8/Math.PI)/2)}function j(u,t,o,v,r){var w=0;var q=t-u;var s=Math.abs(q);var p=o;if(v==false){p+=r}if(p>0&&s>0.01&&s<6.282){w=parseFloat(p)/2/h(q)}return w}e.jqplot.PieRenderer.prototype.drawSlice=function(B,z,y,u,w){if(this._drawData){var p=this._radius;var A=this.fill;var x=this.lineWidth;var s=this.sliceMargin;if(this.fill==false){s+=this.lineWidth}B.save();B.translate(this._center[0],this._center[1]);var D=j(z,y,this.sliceMargin,this.fill,this.lineWidth);var o=D*Math.cos((z+y)/2);var C=D*Math.sin((z+y)/2);if((y-z)<=Math.PI){p-=D}else{p+=D}B.translate(o,C);if(w){for(var v=0,t=this.shadowDepth;v<t;v++){B.save();B.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI),this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));q(p)}for(var v=0,t=this.shadowDepth;v<t;v++){B.restore()}}else{q(p)}B.restore()}function q(r){if(y>6.282+this.startAngle){y=6.282+this.startAngle;if(z>y){z=6.281+this.startAngle}}if(z>=y){return}B.beginPath();B.fillStyle=u;B.strokeStyle=u;B.lineWidth=x;B.arc(0,0,r,z,y,false);B.lineTo(0,0);B.closePath();if(A){B.fill()}else{B.stroke()}}};e.jqplot.PieRenderer.prototype.draw=function(B,z,E,o){var W;var H=(E!=undefined)?E:{};var t=0;var s=0;var N=1;var L=new e.jqplot.ColorGenerator(this.seriesColors);if(E.legendInfo&&E.legendInfo.placement=="insideGrid"){var J=E.legendInfo;switch(J.location){case"nw":t=J.width+J.xoffset;break;case"w":t=J.width+J.xoffset;break;case"sw":t=J.width+J.xoffset;break;case"ne":t=J.width+J.xoffset;N=-1;break;case"e":t=J.width+J.xoffset;N=-1;break;case"se":t=J.width+J.xoffset;N=-1;break;case"n":s=J.height+J.yoffset;break;case"s":s=J.height+J.yoffset;N=-1;break;default:break}}var K=(H.shadow!=undefined)?H.shadow:this.shadow;var A=(H.fill!=undefined)?H.fill:this.fill;var C=B.canvas.width;var I=B.canvas.height;var Q=C-t-2*this.padding;var X=I-s-2*this.padding;var M=Math.min(Q,X);var Y=M;this._sliceAngles=[];var v=this.sliceMargin;if(this.fill==false){v+=this.lineWidth}var q;var G=0;var R,aa,Z,ab;var D=this.startAngle/180*Math.PI;for(var W=0,V=z.length;W<V;W++){aa=(W==0)?D:z[W-1][1]+D;Z=z[W][1]+D;this._sliceAngles.push([aa,Z]);q=j(aa,Z,this.sliceMargin,this.fill,this.lineWidth);if(Math.abs(Z-aa)>Math.PI){G=Math.max(q,G)}}if(this.diameter!=null&&this.diameter>0){this._diameter=this.diameter-2*G}else{this._diameter=Y-2*G}if(this._diameter<6){e.jqplot.log("Diameter of pie too small, not rendering.");return}var S=this._radius=this._diameter/2;this._center=[(C-N*t)/2+N*t+G*Math.cos(D),(I-N*s)/2+N*s+G*Math.sin(D)];if(this.shadow){for(var W=0,V=z.length;W<V;W++){ab="rgba(0,0,0,"+this.shadowAlpha+")";this.renderer.drawSlice.call(this,B,this._sliceAngles[W][0],this._sliceAngles[W][1],ab,true)}}for(var W=0;W<z.length;W++){this.renderer.drawSlice.call(this,B,this._sliceAngles[W][0],this._sliceAngles[W][1],L.next(),false);if(this.showDataLabels&&z[W][2]*100>=this.dataLabelThreshold){var F,U=(this._sliceAngles[W][0]+this._sliceAngles[W][1])/2,T;if(this.dataLabels=="label"){F=this.dataLabelFormatString||"%s";T=e.jqplot.sprintf(F,z[W][0])}else{if(this.dataLabels=="value"){F=this.dataLabelFormatString||"%d";T=e.jqplot.sprintf(F,this.data[W][1])}else{if(this.dataLabels=="percent"){F=this.dataLabelFormatString||"%d%%";T=e.jqplot.sprintf(F,z[W][2]*100)}else{if(this.dataLabels.constructor==Array){F=this.dataLabelFormatString||"%s";T=e.jqplot.sprintf(F,this.dataLabels[W])}}}}var p=(this._radius)*this.dataLabelPositionFactor+this.sliceMargin+this.dataLabelNudge;var P=this._center[0]+Math.cos(U)*p+this.canvas._offsets.left;var O=this._center[1]+Math.sin(U)*p+this.canvas._offsets.top;var u=e('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">'+T+"</div>").insertBefore(o.eventCanvas._elem);if(this.dataLabelCenterOn){P-=u.width()/2;O-=u.height()/2}else{P-=u.width()*Math.sin(U/2);O-=u.height()/2}P=Math.round(P);O=Math.round(O);u.css({left:P,top:O})}}};e.jqplot.PieAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.PieAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.PieAxisRenderer.prototype.constructor=e.jqplot.PieAxisRenderer;e.jqplot.PieAxisRenderer.prototype.init=function(o){this.tickRenderer=e.jqplot.PieTickRenderer;e.extend(true,this,o);this._dataBounds={min:0,max:100};this.min=0;this.max=100;this.showTicks=false;this.ticks=[];this.showMark=false;this.show=false};e.jqplot.PieLegendRenderer=function(){e.jqplot.TableLegendRenderer.call(this)};e.jqplot.PieLegendRenderer.prototype=new e.jqplot.TableLegendRenderer();e.jqplot.PieLegendRenderer.prototype.constructor=e.jqplot.PieLegendRenderer;e.jqplot.PieLegendRenderer.prototype.init=function(o){this.numberRows=null;this.numberColumns=null;e.extend(true,this,o)};e.jqplot.PieLegendRenderer.prototype.draw=function(){var r=this;if(this.show){var B=this._series;this._elem=e(document.createElement("table"));this._elem.addClass("jqplot-table-legend");var E={position:"absolute"};if(this.background){E.background=this.background}if(this.border){E.border=this.border}if(this.fontSize){E.fontSize=this.fontSize}if(this.fontFamily){E.fontFamily=this.fontFamily}if(this.textColor){E.textColor=this.textColor}if(this.marginTop!=null){E.marginTop=this.marginTop}if(this.marginBottom!=null){E.marginBottom=this.marginBottom}if(this.marginLeft!=null){E.marginLeft=this.marginLeft}if(this.marginRight!=null){E.marginRight=this.marginRight}this._elem.css(E);var I=false,A=false,o,y;var C=B[0];var p=new e.jqplot.ColorGenerator(C.seriesColors);if(C.show){var J=C.data;if(this.numberRows){o=this.numberRows;if(!this.numberColumns){y=Math.ceil(J.length/o)}else{y=this.numberColumns}}else{if(this.numberColumns){y=this.numberColumns;o=Math.ceil(J.length/this.numberColumns)}else{o=J.length;y=1}}var H,G;var q,w,v;var x,z,F;var D=0;var u,t;for(H=0;H<o;H++){q=e(document.createElement("tr"));q.addClass("jqplot-table-legend");if(A){q.prependTo(this._elem)}else{q.appendTo(this._elem)}for(G=0;G<y;G++){if(D<J.length){x=this.labels[D]||J[D][0].toString();F=p.next();if(!A){if(H>0){I=true}else{I=false}}else{if(H==o-1){I=false}else{I=true}}z=(I)?this.rowSpacing:"0";w=e(document.createElement("td"));w.addClass("jqplot-table-legend jqplot-table-legend-swatch");w.css({textAlign:"center",paddingTop:z});u=e(document.createElement("div"));u.addClass("jqplot-table-legend-swatch-outline");t=e(document.createElement("div"));t.addClass("jqplot-table-legend-swatch");t.css({backgroundColor:F,borderColor:F});w.append(u.append(t));v=e(document.createElement("td"));v.addClass("jqplot-table-legend jqplot-table-legend-label");v.css("paddingTop",z);if(this.escapeHtml){v.text(x)}else{v.html(x)}if(A){v.prependTo(q);w.prependTo(q)}else{w.appendTo(q);v.appendTo(q)}I=true}D++}}}}return this._elem};e.jqplot.PieRenderer.prototype.handleMove=function(q,p,t,s,r){if(s){var o=[s.seriesIndex,s.pointIndex,s.data];r.target.trigger("jqplotDataMouseOver",o);if(r.series[o[0]].highlightMouseOver&&!(o[0]==r.plugins.pieRenderer.highlightedSeriesIndex&&o[1]==r.series[o[0]]._highlightedPoint)){r.target.trigger("jqplotDataHighlight",o);d(r,o[0],o[1])}}else{if(s==null){k(r)}}};function c(s,r,p){p=p||{};p.axesDefaults=p.axesDefaults||{};p.legend=p.legend||{};p.seriesDefaults=p.seriesDefaults||{};var o=false;if(p.seriesDefaults.renderer==e.jqplot.PieRenderer){o=true}else{if(p.series){for(var q=0;q<p.series.length;q++){if(p.series[q].renderer==e.jqplot.PieRenderer){o=true}}}}if(o){p.axesDefaults.renderer=e.jqplot.PieAxisRenderer;p.legend.renderer=e.jqplot.PieLegendRenderer;p.legend.preDraw=true;p.seriesDefaults.pointLabels={show:false}}}function g(r,q,o){for(var p=0;p<this.series.length;p++){if(this.series[p].renderer.constructor==e.jqplot.PieRenderer){if(this.series[p].highlightMouseOver){this.series[p].highlightMouseDown=false}}}}function m(o){for(var p=0;p<this.series.length;p++){this.series[p].seriesColors=this.seriesColors;this.series[p].colorGenerator=e.jqplot.colorGenerator}}function d(t,r,q){var p=t.series[r];var o=t.plugins.pieRenderer.highlightCanvas;o._ctx.clearRect(0,0,o._ctx.canvas.width,o._ctx.canvas.height);p._highlightedPoint=q;t.plugins.pieRenderer.highlightedSeriesIndex=r;p.renderer.drawSlice.call(p,o._ctx,p._sliceAngles[q][0],p._sliceAngles[q][1],p.highlightColorGenerator.get(q),false)}function k(q){var o=q.plugins.pieRenderer.highlightCanvas;o._ctx.clearRect(0,0,o._ctx.canvas.width,o._ctx.canvas.height);for(var p=0;p<q.series.length;p++){q.series[p]._highlightedPoint=null}q.plugins.pieRenderer.highlightedSeriesIndex=null;q.target.trigger("jqplotDataUnhighlight")}function b(s,r,v,u,t){if(u){var q=[u.seriesIndex,u.pointIndex,u.data];var p=jQuery.Event("jqplotDataMouseOver");p.pageX=s.pageX;p.pageY=s.pageY;t.target.trigger(p,q);if(t.series[q[0]].highlightMouseOver&&!(q[0]==t.plugins.pieRenderer.highlightedSeriesIndex&&q[1]==t.series[q[0]]._highlightedPoint)){var o=jQuery.Event("jqplotDataHighlight");o.which=s.which;o.pageX=s.pageX;o.pageY=s.pageY;t.target.trigger(o,q);d(t,q[0],q[1])}}else{if(u==null){k(t)}}}function a(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];if(s.series[p[0]].highlightMouseDown&&!(p[0]==s.plugins.pieRenderer.highlightedSeriesIndex&&p[1]==s.series[p[0]]._highlightedPoint)){var o=jQuery.Event("jqplotDataHighlight");o.which=r.which;o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p);d(s,p[0],p[1])}}else{if(t==null){k(s)}}}function l(q,p,t,s,r){var o=r.plugins.pieRenderer.highlightedSeriesIndex;if(o!=null&&r.series[o].highlightMouseDown){k(r)}}function f(r,q,u,t,s){if(t){var p=[t.seriesIndex,t.pointIndex,t.data];var o=jQuery.Event("jqplotDataClick");o.which=r.which;o.pageX=r.pageX;o.pageY=r.pageY;s.target.trigger(o,p)}}function n(s,r,v,u,t){if(u){var q=[u.seriesIndex,u.pointIndex,u.data];var o=t.plugins.pieRenderer.highlightedSeriesIndex;if(o!=null&&t.series[o].highlightMouseDown){k(t)}var p=jQuery.Event("jqplotDataRightClick");p.which=s.which;p.pageX=s.pageX;p.pageY=s.pageY;t.target.trigger(p,q)}}function i(){if(this.plugins.pieRenderer&&this.plugins.pieRenderer.highlightCanvas){this.plugins.pieRenderer.highlightCanvas.resetCanvas();this.plugins.pieRenderer.highlightCanvas=null}this.plugins.pieRenderer={highlightedSeriesIndex:null};this.plugins.pieRenderer.highlightCanvas=new e.jqplot.GenericCanvas();var p=e(this.targetId+" .jqplot-data-label");if(p.length){e(p[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-pieRenderer-highlight-canvas",this._plotDimensions,this))}else{this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-pieRenderer-highlight-canvas",this._plotDimensions,this))}var o=this.plugins.pieRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(q){k(q.data.plot)})}e.jqplot.preInitHooks.push(c);e.jqplot.PieTickRenderer=function(){e.jqplot.AxisTickRenderer.call(this)};e.jqplot.PieTickRenderer.prototype=new e.jqplot.AxisTickRenderer();e.jqplot.PieTickRenderer.prototype.constructor=e.jqplot.PieTickRenderer})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pointLabels.js b/web/extensions/jqplot/plugins/jqplot.pointLabels.js new file mode 100644 index 0000000..4edc69e --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pointLabels.js @@ -0,0 +1,377 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + /** + * Class: $.jqplot.PointLabels + * Plugin for putting labels at the data points. + * + * To use this plugin, include the js + * file in your source: + * + * > <script type="text/javascript" src="plugins/jqplot.pointLabels.js"></script> + * + * By default, the last value in the data ponit array in the data series is used + * for the label. For most series renderers, extra data can be added to the + * data point arrays and the last value will be used as the label. + * + * For instance, + * this series: + * + * > [[1,4], [3,5], [7,2]] + * + * Would, by default, use the y values in the labels. + * Extra data can be added to the series like so: + * + * > [[1,4,'mid'], [3 5,'hi'], [7,2,'low']] + * + * And now the point labels would be 'mid', 'low', and 'hi'. + * + * Options to the point labels and a custom labels array can be passed into the + * "pointLabels" option on the series option like so: + * + * > series:[{pointLabels:{ + * > labels:['mid', 'hi', 'low'], + * > location:'se', + * > ypadding: 12 + * > } + * > }] + * + * A custom labels array in the options takes precendence over any labels + * in the series data. If you have a custom labels array in the options, + * but still want to use values from the series array as labels, set the + * "labelsFromSeries" option to true. + * + * By default, html entities (<, >, etc.) are escaped in point labels. + * If you want to include actual html markup in the labels, + * set the "escapeHTML" option to false. + * + */ + $.jqplot.PointLabels = function(options) { + // Group: Properties + // + // prop: show + // show the labels or not. + this.show = $.jqplot.config.enablePlugins; + // prop: location + // compass location where to position the label around the point. + // 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' + this.location = 'n'; + // prop: labelsFromSeries + // true to use labels within data point arrays. + this.labelsFromSeries = false; + // prop: seriesLabelIndex + // array index for location of labels within data point arrays. + // if null, will use the last element of the data point array. + this.seriesLabelIndex = null; + // prop: labels + // array of arrays of labels, one array for each series. + this.labels = []; + // actual labels that will get displayed. + // needed to preserve user specified labels in labels array. + this._labels = []; + // prop: stackedValue + // true to display value as stacked in a stacked plot. + // no effect if labels is specified. + this.stackedValue = false; + // prop: ypadding + // vertical padding in pixels between point and label + this.ypadding = 6; + // prop: xpadding + // horizontal padding in pixels between point and label + this.xpadding = 6; + // prop: escapeHTML + // true to escape html entities in the labels. + // If you want to include markup in the labels, set to false. + this.escapeHTML = true; + // prop: edgeTolerance + // Number of pixels that the label must be away from an axis + // boundary in order to be drawn. Negative values will allow overlap + // with the grid boundaries. + this.edgeTolerance = -5; + // prop: formatter + // A class of a formatter for the tick text. sprintf by default. + this.formatter = $.jqplot.DefaultTickFormatter; + // prop: formatString + // string passed to the formatter. + this.formatString = ''; + // prop: hideZeros + // true to not show a label for a value which is 0. + this.hideZeros = false; + this._elems = []; + + $.extend(true, this, options); + }; + + var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; + var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; + var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; + + // called with scope of a series + $.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts, plot){ + var options = $.extend(true, {}, seriesDefaults, opts); + options.pointLabels = options.pointLabels || {}; + if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal' && !options.pointLabels.location) { + options.pointLabels.location = 'e'; + } + // add a pointLabels attribute to the series plugins + this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); + this.plugins.pointLabels.setLabels.call(this); + }; + + // called with scope of series + $.jqplot.PointLabels.prototype.setLabels = function() { + var p = this.plugins.pointLabels; + var labelIdx; + if (p.seriesLabelIndex != null) { + labelIdx = p.seriesLabelIndex; + } + else if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal') { + labelIdx = (this._plotData[0].length < 3) ? 0 : this._plotData[0].length -1; + } + else { + labelIdx = (this._plotData.length === 0) ? 0 : this._plotData[0].length -1; + } + p._labels = []; + if (p.labels.length === 0 || p.labelsFromSeries) { + if (p.stackedValue) { + if (this._plotData.length && this._plotData[0].length){ + // var idx = p.seriesLabelIndex || this._plotData[0].length -1; + for (var i=0; i<this._plotData.length; i++) { + p._labels.push(this._plotData[i][labelIdx]); + } + } + } + else { + // var d = this._plotData; + var d = this.data; + if (this.renderer.constructor === $.jqplot.BarRenderer && this.waterfall) { + d = this._data; + } + if (d.length && d[0].length) { + // var idx = p.seriesLabelIndex || d[0].length -1; + for (var i=0; i<d.length; i++) { + p._labels.push(d[i][labelIdx]); + } + } + d = null; + } + } + else if (p.labels.length){ + p._labels = p.labels; + } + }; + + $.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) { + location = location || this.location; + padding = padding || this.xpadding; + var offset; + + switch (location) { + case 'nw': + offset = -elem.outerWidth(true) - this.xpadding; + break; + case 'n': + offset = -elem.outerWidth(true)/2; + break; + case 'ne': + offset = this.xpadding; + break; + case 'e': + offset = this.xpadding; + break; + case 'se': + offset = this.xpadding; + break; + case 's': + offset = -elem.outerWidth(true)/2; + break; + case 'sw': + offset = -elem.outerWidth(true) - this.xpadding; + break; + case 'w': + offset = -elem.outerWidth(true) - this.xpadding; + break; + default: // same as 'nw' + offset = -elem.outerWidth(true) - this.xpadding; + break; + } + return offset; + }; + + $.jqplot.PointLabels.prototype.yOffset = function(elem, location, padding) { + location = location || this.location; + padding = padding || this.xpadding; + var offset; + + switch (location) { + case 'nw': + offset = -elem.outerHeight(true) - this.ypadding; + break; + case 'n': + offset = -elem.outerHeight(true) - this.ypadding; + break; + case 'ne': + offset = -elem.outerHeight(true) - this.ypadding; + break; + case 'e': + offset = -elem.outerHeight(true)/2; + break; + case 'se': + offset = this.ypadding; + break; + case 's': + offset = this.ypadding; + break; + case 'sw': + offset = this.ypadding; + break; + case 'w': + offset = -elem.outerHeight(true)/2; + break; + default: // same as 'nw' + offset = -elem.outerHeight(true) - this.ypadding; + break; + } + return offset; + }; + + // called with scope of series + $.jqplot.PointLabels.draw = function (sctx, options, plot) { + var p = this.plugins.pointLabels; + // set labels again in case they have changed. + p.setLabels.call(this); + // remove any previous labels + for (var i=0; i<p._elems.length; i++) { + // Memory Leaks patch + // p._elems[i].remove(); + p._elems[i].emptyForce(); + } + p._elems.splice(0, p._elems.length); + + if (p.show) { + var ax = '_'+this._stackAxis+'axis'; + + if (!p.formatString) { + p.formatString = this[ax]._ticks[0].formatString; + p.formatter = this[ax]._ticks[0].formatter; + } + + var pd = this._plotData; + var ppd = this._prevPlotData; + var xax = this._xaxis; + var yax = this._yaxis; + var elem, helem; + + for (var i=0, l=p._labels.length; i < l; i++) { + var label = p._labels[i]; + + if (label == null || (p.hideZeros && parseInt(label, 10) == 0)) { + continue; + } + + label = p.formatter(p.formatString, label); + + helem = document.createElement('div'); + p._elems[i] = $(helem); + + elem = p._elems[i]; + + + elem.addClass('jqplot-point-label jqplot-series-'+this.index+' jqplot-point-'+i); + elem.css('position', 'absolute'); + elem.insertAfter(sctx.canvas); + + if (p.escapeHTML) { + elem.text(label); + } + else { + elem.html(label); + } + var location = p.location; + if ((this.fillToZero && pd[i][1] < 0) || (this.fillToZero && this._type === 'bar' && this.barDirection === 'horizontal' && pd[i][0] < 0) || (this.waterfall && parseInt(label, 10)) < 0) { + location = oppositeLocations[locationIndicies[location]]; + } + + + var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location); + var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location); + + // we have stacked chart but are not showing stacked values, + // place labels in center. + if (this._stack && !p.stackedValue) { + if (this.barDirection === "vertical") { + elt = (this._barPoints[i][0][1] + this._barPoints[i][1][1]) / 2 + plot._gridPadding.top - 0.5 * elem.outerHeight(true); + } + else { + ell = (this._barPoints[i][2][0] + this._barPoints[i][0][0]) / 2 + plot._gridPadding.left - 0.5 * elem.outerWidth(true); + } + } + + if (this.renderer.constructor == $.jqplot.BarRenderer) { + if (this.barDirection == "vertical") { + ell += this._barNudge; + } + else { + elt -= this._barNudge; + } + } + elem.css('left', ell); + elem.css('top', elt); + var elr = ell + elem.width(); + var elb = elt + elem.height(); + var et = p.edgeTolerance; + var scl = $(sctx.canvas).position().left; + var sct = $(sctx.canvas).position().top; + var scr = sctx.canvas.width + scl; + var scb = sctx.canvas.height + sct; + // if label is outside of allowed area, remove it + if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) { + elem.remove(); + } + + elem = null; + helem = null; + } + + // finally, animate them if the series is animated + // if (this.renderer.animation && this.renderer.animation._supported && this.renderer.animation.show && plot._drawCount < 2) { + // var sel = '.jqplot-point-label.jqplot-series-'+this.index; + // $(sel).hide(); + // $(sel).fadeIn(1000); + // } + + } + }; + + $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init); + $.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw); +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pointLabels.min.js b/web/extensions/jqplot/plugins/jqplot.pointLabels.min.js new file mode 100644 index 0000000..01a7562 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pointLabels.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(c){c.jqplot.PointLabels=function(e){this.show=c.jqplot.config.enablePlugins;this.location="n";this.labelsFromSeries=false;this.seriesLabelIndex=null;this.labels=[];this._labels=[];this.stackedValue=false;this.ypadding=6;this.xpadding=6;this.escapeHTML=true;this.edgeTolerance=-5;this.formatter=c.jqplot.DefaultTickFormatter;this.formatString="";this.hideZeros=false;this._elems=[];c.extend(true,this,e)};var a=["nw","n","ne","e","se","s","sw","w"];var d={nw:0,n:1,ne:2,e:3,se:4,s:5,sw:6,w:7};var b=["se","s","sw","w","nw","n","ne","e"];c.jqplot.PointLabels.init=function(j,h,f,g,i){var e=c.extend(true,{},f,g);e.pointLabels=e.pointLabels||{};if(this.renderer.constructor===c.jqplot.BarRenderer&&this.barDirection==="horizontal"&&!e.pointLabels.location){e.pointLabels.location="e"}this.plugins.pointLabels=new c.jqplot.PointLabels(e.pointLabels);this.plugins.pointLabels.setLabels.call(this)};c.jqplot.PointLabels.prototype.setLabels=function(){var f=this.plugins.pointLabels;var h;if(f.seriesLabelIndex!=null){h=f.seriesLabelIndex}else{if(this.renderer.constructor===c.jqplot.BarRenderer&&this.barDirection==="horizontal"){h=(this._plotData[0].length<3)?0:this._plotData[0].length-1}else{h=(this._plotData.length===0)?0:this._plotData[0].length-1}}f._labels=[];if(f.labels.length===0||f.labelsFromSeries){if(f.stackedValue){if(this._plotData.length&&this._plotData[0].length){for(var e=0;e<this._plotData.length;e++){f._labels.push(this._plotData[e][h])}}}else{var g=this.data;if(this.renderer.constructor===c.jqplot.BarRenderer&&this.waterfall){g=this._data}if(g.length&&g[0].length){for(var e=0;e<g.length;e++){f._labels.push(g[e][h])}}g=null}}else{if(f.labels.length){f._labels=f.labels}}};c.jqplot.PointLabels.prototype.xOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerWidth(true)-this.xpadding;break;case"n":h=-f.outerWidth(true)/2;break;case"ne":h=this.xpadding;break;case"e":h=this.xpadding;break;case"se":h=this.xpadding;break;case"s":h=-f.outerWidth(true)/2;break;case"sw":h=-f.outerWidth(true)-this.xpadding;break;case"w":h=-f.outerWidth(true)-this.xpadding;break;default:h=-f.outerWidth(true)-this.xpadding;break}return h};c.jqplot.PointLabels.prototype.yOffset=function(f,e,g){e=e||this.location;g=g||this.xpadding;var h;switch(e){case"nw":h=-f.outerHeight(true)-this.ypadding;break;case"n":h=-f.outerHeight(true)-this.ypadding;break;case"ne":h=-f.outerHeight(true)-this.ypadding;break;case"e":h=-f.outerHeight(true)/2;break;case"se":h=this.ypadding;break;case"s":h=this.ypadding;break;case"sw":h=this.ypadding;break;case"w":h=-f.outerHeight(true)/2;break;default:h=-f.outerHeight(true)-this.ypadding;break}return h};c.jqplot.PointLabels.draw=function(x,j,v){var t=this.plugins.pointLabels;t.setLabels.call(this);for(var w=0;w<t._elems.length;w++){t._elems[w].emptyForce()}t._elems.splice(0,t._elems.length);if(t.show){var r="_"+this._stackAxis+"axis";if(!t.formatString){t.formatString=this[r]._ticks[0].formatString;t.formatter=this[r]._ticks[0].formatter}var E=this._plotData;var D=this._prevPlotData;var A=this._xaxis;var q=this._yaxis;var z,f;for(var w=0,u=t._labels.length;w<u;w++){var o=t._labels[w];if(o==null||(t.hideZeros&&parseInt(o,10)==0)){continue}o=t.formatter(t.formatString,o);f=document.createElement("div");t._elems[w]=c(f);z=t._elems[w];z.addClass("jqplot-point-label jqplot-series-"+this.index+" jqplot-point-"+w);z.css("position","absolute");z.insertAfter(x.canvas);if(t.escapeHTML){z.text(o)}else{z.html(o)}var g=t.location;if((this.fillToZero&&E[w][1]<0)||(this.fillToZero&&this._type==="bar"&&this.barDirection==="horizontal"&&E[w][0]<0)||(this.waterfall&&parseInt(o,10))<0){g=b[d[g]]}var n=A.u2p(E[w][0])+t.xOffset(z,g);var h=q.u2p(E[w][1])+t.yOffset(z,g);if(this._stack&&!t.stackedValue){if(this.barDirection==="vertical"){h=(this._barPoints[w][0][1]+this._barPoints[w][1][1])/2+v._gridPadding.top-0.5*z.outerHeight(true)}else{n=(this._barPoints[w][2][0]+this._barPoints[w][0][0])/2+v._gridPadding.left-0.5*z.outerWidth(true)}}if(this.renderer.constructor==c.jqplot.BarRenderer){if(this.barDirection=="vertical"){n+=this._barNudge}else{h-=this._barNudge}}z.css("left",n);z.css("top",h);var k=n+z.width();var s=h+z.height();var C=t.edgeTolerance;var e=c(x.canvas).position().left;var y=c(x.canvas).position().top;var B=x.canvas.width+e;var m=x.canvas.height+y;if(n-C<e||h-C<y||k+C>B||s+C>m){z.remove()}z=null;f=null}}};c.jqplot.postSeriesInitHooks.push(c.jqplot.PointLabels.init);c.jqplot.postDrawSeriesHooks.push(c.jqplot.PointLabels.draw)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.js b/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.js new file mode 100644 index 0000000..7650e30 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.js @@ -0,0 +1,728 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + $.jqplot.PyramidAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.PyramidAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.PyramidAxisRenderer.prototype.constructor = $.jqplot.PyramidAxisRenderer; + + // called with scope of axis + $.jqplot.PyramidAxisRenderer.prototype.init = function(options){ + // Group: Properties + // + // prop: position + // Position of axis. Values are: top, bottom , left, center, right. + // By default, x and x2 axes are bottom, y axis is center. + this.position = null; + // prop: drawBaseline + // True to draw the axis baseline. + this.drawBaseline = true; + // prop: baselineWidth + // width of the baseline in pixels. + this.baselineWidth = null; + // prop: baselineColor + // CSS color spec for the baseline. + this.baselineColor = null; + this.tickSpacingFactor = 25; + this._type = 'pyramid'; + this._splitAxis = false; + this._splitLength = null; + this.category = false; + this._autoFormatString = ''; + this._overrideFormatString = false; + + $.extend(true, this, options); + this.renderer.options = options; + + this.resetDataBounds = this.renderer.resetDataBounds; + this.resetDataBounds(); + + }; + + $.jqplot.PyramidAxisRenderer.prototype.resetDataBounds = function() { + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + var db = this._dataBounds; + db.min = null; + db.max = null; + var temp; + for (var i=0; i<this._series.length; i++) { + var s = this._series[i]; + var d = s._plotData; + + for (var j=0, l=d.length; j<l; j++) { + if (this.name.charAt(0) === 'x') { + temp = d[j][1]; + if ((temp !== null && temp < db.min) || db.min === null) { + db.min = temp; + } + if ((temp !== null && temp > db.max) || db.max === null) { + db.max = temp; + } + } + else { + temp = d[j][0]; + if ((temp !== null && temp < db.min) || db.min === null) { + db.min = temp; + } + if ((temp !== null && temp > db.max) || db.max === null) { + db.max = temp; + } + } + } + } + }; + + // called with scope of axis + $.jqplot.PyramidAxisRenderer.prototype.draw = function(ctx, plot) { + if (this.show) { + // populate the axis label and value properties. + // createTicks is a method on the renderer, but + // call it within the scope of the axis. + this.renderer.createTicks.call(this, plot); + // fill a div with axes labels in the right direction. + // Need to pregenerate each axis to get its bounds and + // position it and the labels correctly on the plot. + var dim=0; + var temp; + // Added for theming. + if (this._elem) { + // Memory Leaks patch + //this._elem.empty(); + this._elem.emptyForce(); + this._elem = null; + } + + this._elem = $(document.createElement('div')); + this._elem.addClass('jqplot-axis jqplot-'+this.name); + this._elem.css('position', 'absolute'); + + + if (this.name == 'xaxis' || this.name == 'x2axis') { + this._elem.width(this._plotDimensions.width); + } + else { + this._elem.height(this._plotDimensions.height); + } + + // create a _label object. + this.labelOptions.axis = this.name; + this._label = new this.labelRenderer(this.labelOptions); + if (this._label.show) { + var elem = this._label.draw(ctx, plot); + elem.appendTo(this._elem); + elem = null; + } + + var t = this._ticks; + var tick; + for (var i=0; i<t.length; i++) { + tick = t[i]; + if (tick.show && tick.showLabel && (!tick.isMinorTick)) { + this._elem.append(tick.draw(ctx, plot)); + } + } + tick = null; + t = null; + } + return this._elem; + }; + + // Note, primes can be found on http://primes.utm.edu/ + var _primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; + + + var _primesHash = {}; + + for (var i =0, l = _primes.length; i < l; i++) { + _primesHash[_primes[i]] = _primes[i]; + } + + // called with scope of axis + $.jqplot.PyramidAxisRenderer.prototype.createTicks = function(plot) { + // we're are operating on an axis here + var userTicks = this.ticks; + // databounds were set on axis initialization. + var db = this._dataBounds; + var dim; + var interval; + var min; + var max; + var range; + var pos1; + var pos2; + var tt; + var i; + var l; + var s; + // get a copy of user's settings for min/max. + var userMin = this.min; + var userMax = this.max; + var ut; + var t; + var threshold; + var tdim; + var scalefact; + var ret; + var tumin; + var tumax; + var maxVisibleTicks; + var val; + var skip = null; + var temp; + + // if we already have ticks, use them. + // ticks must be in order of increasing value. + + if (userTicks.length) { + // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed + for (i=0, l=userTicks.length; i<l; i++){ + ut = userTicks[i]; + t = new this.tickRenderer(this.tickOptions); + if ($.isArray(ut)) { + t.value = ut[0]; + t.label = ut[1]; + t.setTick(ut[0], this.name); + this._ticks.push(t); + } + + else if ($.isPlainObject(ut)) { + $.extend(true, t, ut); + t.axis = this.name; + this._ticks.push(t); + } + + else { + if (typeof ut === 'string') { + val = i + plot.defaultAxisStart; + } + else { + val = ut; + } + t.value = val; + t.label = ut; + t.axis = this.name; + this._ticks.push(t); + } + } + this.numberTicks = userTicks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this.numberTicks-1].value; + this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); + + // use user specified tickInterval if there is one + if (this._options.tickInterval) { + // hide every tick except for ticks on interval + var ti = this._options.tickInterval; + for (i=0; i<this.numberTicks; i++) { + if (i%ti !== 0) { + // this._ticks[i].show = false; + this._ticks[i].isMinorTick = true; + } + } + } + + else { + // check if we have too many ticks + dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; + maxVisibleTicks = Math.round(2.0 + dim/this.tickSpacingFactor); + + if (this.numberTicks > maxVisibleTicks) { + // check for number of ticks we can skip + temp = this.numberTicks - 1; + for (i=2; i<temp; i++) { + if (temp % i === 0 && temp/i < maxVisibleTicks) { + skip = i-1; + break; + } + } + + if (skip !== null) { + var count = 1; + for (i=1, l=this._ticks.length; i<l; i++) { + if (count <= skip) { + this._ticks[i].show = false; + count += 1; + } + else { + count = 1; + } + } + } + } + } + + // if category style, add minor ticks in between + temp = []; + if (this.category) { + // turn off gridline and mark on first tick + this._ticks[0].showGridline = false; + this._ticks[0].showMark = false; + + for (i=this._ticks.length-1; i>0; i--) { + t = new this.tickRenderer(this.tickOptions); + t.value = this._ticks[i-1].value + this.tickInterval/2.0; + t.label = ''; + t.showLabel = false; + t.axis = this.name; + this._ticks[i].showGridline = false; + this._ticks[i].showMark = false; + this._ticks.splice(i, 0, t); + // temp.push(t); + } + + // merge in the new ticks + // for (i=1, l=temp.length; i<l; i++) { + // this._ticks.splice(i, 0, temp[i]); + // } + + // now add a tick at beginning and end + t = new this.tickRenderer(this.tickOptions); + t.value = this._ticks[0].value - this.tickInterval/2.0; + t.label = ''; + t.showLabel = false; + t.axis = this.name; + this._ticks.unshift(t); + + t = new this.tickRenderer(this.tickOptions); + t.value = this._ticks[this._ticks.length-1].value + this.tickInterval/2.0; + t.label = ''; + t.showLabel = false; + t.axis = this.name; + this._ticks.push(t); + + this.tickInterval = this.tickInterval / 2.0; + this.numberTicks = this._ticks.length; + this.min = this._ticks[0].value; + this.max = this._ticks[this._ticks.length-1].value; + } + } + + // we don't have any ticks yet, let's make some! + else { + if (this.name.charAt(0) === 'x') { + dim = this._plotDimensions.width; + // make sure x axis is symetric about 0. + var tempmax = Math.max(db.max, Math.abs(db.min)); + var tempmin = Math.min(db.min, -tempmax); + // min = ((this.min != null) ? this.min : tempmin); + // max = ((this.max != null) ? this.max : tempmax); + min = tempmin; + max = tempmax; + range = max - min; + + if (this.tickOptions == null || !this.tickOptions.formatString) { + this._overrideFormatString = true; + } + + threshold = 30; + tdim = Math.max(dim, threshold+1); + scalefact = (tdim-threshold)/300.0; + ret = $.jqplot.LinearTickGenerator(min, max, scalefact); + // calculate a padded max and min, points should be less than these + // so that they aren't too close to the edges of the plot. + // User can adjust how much padding is allowed with pad, padMin and PadMax options. + tumin = min + range*(this.padMin - 1); + tumax = max - range*(this.padMax - 1); + + if (min < tumin || max > tumax) { + tumin = min - range*(this.padMin - 1); + tumax = max + range*(this.padMax - 1); + ret = $.jqplot.LinearTickGenerator(tumin, tumax, scalefact); + } + + this.min = ret[0]; + this.max = ret[1]; + this.numberTicks = ret[2]; + this._autoFormatString = ret[3]; + this.tickInterval = ret[4]; + } + else { + dim = this._plotDimensions.height; + + // ticks will be on whole integers like 1, 2, 3, ... or 1, 4, 7, ... + min = db.min; + max = db.max; + s = this._series[0]; + this._ticks = []; + + range = max - min; + + // if range is a prime, will get only 2 ticks, expand range in that case. + if (_primesHash[range]) { + range += 1; + max += 1; + } + + this.max = max; + this.min = min; + + maxVisibleTicks = Math.round(2.0 + dim/this.tickSpacingFactor); + + if (range + 1 <= maxVisibleTicks) { + this.numberTicks = range + 1; + this.tickInterval = 1.0; + } + + else { + // figure out a round number of ticks to skip in every interval + // range / ti + 1 = nt + // ti = range / (nt - 1) + for (var i=maxVisibleTicks; i>1; i--) { + if (range/(i - 1) === Math.round(range/(i - 1))) { + this.numberTicks = i; + this.tickInterval = range/(i - 1); + break; + } + + } + } + } + + if (this._overrideFormatString && this._autoFormatString != '') { + this.tickOptions = this.tickOptions || {}; + this.tickOptions.formatString = this._autoFormatString; + } + + var labelval; + for (i=0; i<this.numberTicks; i++) { + this.tickOptions.axis = this.name; + labelval = this.min + this.tickInterval * i; + if (this.name.charAt(0) === 'x') { + labelval = Math.abs(labelval); + } + // this.tickOptions.label = String (labelval); + this.tickOptions.value = this.min + this.tickInterval * i; + t = new this.tickRenderer(this.tickOptions); + + t.label = t.prefix + t.formatter(t.formatString, labelval); + + this._ticks.push(t); + // for x axis, if y axis is in middle, add a symetrical 0 tick + if (this.name.charAt(0) === 'x' && plot.axes.yMidAxis.show && this.tickOptions.value === 0) { + this._splitAxis = true; + this._splitLength = plot.axes.yMidAxis.getWidth(); + // t.value = -this.max/2000.0; + t = new this.tickRenderer(this.tickOptions); + this._ticks.push(t); + t.value = this.max/2000.0; + } + } + t = null; + } + }; + + // called with scope of axis + $.jqplot.PyramidAxisRenderer.prototype.set = function() { + var dim = 0; + var temp; + var w = 0; + var h = 0; + var i; + var t; + var tick; + var lshow = (this._label == null) ? false : this._label.show; + if (this.show) { + t = this._ticks; + l = t.length; + for (i=0; i<l; i++) { + tick = t[i]; + if (!tick._breakTick && tick.show && tick.showLabel && !tick.isMinorTick) { + if (this.name.charAt(0) === 'x') { + temp = tick._elem.outerHeight(true); + } + else { + temp = tick._elem.outerWidth(true); + } + if (temp > dim) { + dim = temp; + } + } + } + + if (this.name === 'yMidAxis') { + for (i=0; i<l; i++) { + tick = t[i]; + if (tick._elem) { + temp = (dim - tick._elem.outerWidth(true))/2.0; + tick._elem.css('left', temp); + } + } + } + tick = null; + t = null; + + if (lshow) { + w = this._label._elem.outerWidth(true); + h = this._label._elem.outerHeight(true); + } + if (this.name === 'xaxis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); + } + else if (this.name === 'x2axis') { + dim = dim + h; + this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); + } + else if (this.name === 'yaxis') { + dim = dim + w; + this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + else if (this.name === 'yMidAxis') { + // don't include width of label at all in width of axis? + // dim = (dim > w) ? dim : w; + var temp = dim/2.0 - w/2.0; + this._elem.css({'width':dim+'px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css({width: w, left: temp, top: 0}); + } + } + else { + dim = dim + w; + this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); + if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { + this._label._elem.css('width', w+'px'); + } + } + } + }; + + $.jqplot.PyramidAxisRenderer.prototype.pack = function(pos, offsets) { + // Add defaults for repacking from resetTickValues function. + pos = pos || {}; + offsets = offsets || this._offsets; + + var ticks = this._ticks; + var max = this.max; + var min = this.min; + var offmax = offsets.max; + var offmin = offsets.min; + var lshow = (this._label == null) ? false : this._label.show; + + for (var p in pos) { + this._elem.css(p, pos[p]); + } + + this._offsets = offsets; + // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. + var pixellength = offmax - offmin; + var unitlength = max - min; + var sl = this._splitLength; + + // point to unit and unit to point conversions references to Plot DOM element top left corner. + if (this._splitAxis) { + pixellength -= this._splitLength; + + // don't know that this one is correct. + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + this.u2p = function(u){ + if (u <= 0) { + return (u - min) * pixellength / unitlength + offmin; + } + else { + return (u - min) * pixellength / unitlength + offmin + sl; + } + }; + + this.series_u2p = function(u){ + if (u <= 0) { + return (u - min) * pixellength / unitlength; + } + else { + return (u - min) * pixellength / unitlength + sl; + } + }; + + // don't know that this one is correct. + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + else { + this.p2u = function(p){ + return (p - offmin) * unitlength / pixellength + min; + }; + + this.u2p = function(u){ + return (u - min) * pixellength / unitlength + offmin; + }; + + if (this.name.charAt(0) === 'x'){ + this.series_u2p = function(u){ + return (u - min) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + min; + }; + } + + else { + this.series_u2p = function(u){ + return (u - max) * pixellength / unitlength; + }; + this.series_p2u = function(p){ + return p * unitlength / pixellength + max; + }; + } + } + + if (this.show) { + if (this.name.charAt(0) === 'x') { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel) { + var shim; + + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'xaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + if (temp * t.angle < 0) { + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + } + // position at start + else { + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + } + break; + case 'end': + shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + case 'start': + shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + break; + case 'middle': + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + default: + shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + break; + } + } + else { + shim = -t.getWidth()/2; + } + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('left', val); + t.pack(); + } + } + if (lshow) { + var w = this._label._elem.outerWidth(true); + this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); + if (this.name == 'xaxis') { + this._label._elem.css('bottom', '0px'); + } + else { + this._label._elem.css('top', '0px'); + } + this._label.pack(); + } + } + else { + for (var i=0; i<ticks.length; i++) { + var t = ticks[i]; + if (t.show && t.showLabel && !t.isMinorTick) { + var shim; + if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { + // will need to adjust auto positioning based on which axis this is. + var temp = (this.name == 'yaxis') ? 1 : -1; + switch (t.labelPosition) { + case 'auto': + // position at end + case 'end': + if (temp * t.angle < 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'start': + if (t.angle > 0) { + shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; + } + else { + shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; + } + break; + case 'middle': + // if (t.angle > 0) { + // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; + // } + // else { + // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; + // } + shim = -t.getHeight()/2; + break; + default: + shim = -t.getHeight()/2; + break; + } + } + else { + shim = -t.getHeight()/2; + } + + var val = this.u2p(t.value) + shim + 'px'; + t._elem.css('top', val); + t.pack(); + } + } + if (lshow) { + var h = this._label._elem.outerHeight(true); + if (this.name !== 'yMidAxis') { + this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); + } + if (this.name == 'yaxis') { + this._label._elem.css('left', '0px'); + } + else if (this.name !== 'yMidAxis') { + this._label._elem.css('right', '0px'); + } + this._label.pack(); + } + } + } + + ticks = null; + }; +})(jQuery); diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.min.js new file mode 100644 index 0000000..7b5db94 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidAxisRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(e){e.jqplot.PyramidAxisRenderer=function(){e.jqplot.LinearAxisRenderer.call(this)};e.jqplot.PyramidAxisRenderer.prototype=new e.jqplot.LinearAxisRenderer();e.jqplot.PyramidAxisRenderer.prototype.constructor=e.jqplot.PyramidAxisRenderer;e.jqplot.PyramidAxisRenderer.prototype.init=function(f){this.position=null;this.drawBaseline=true;this.baselineWidth=null;this.baselineColor=null;this.tickSpacingFactor=25;this._type="pyramid";this._splitAxis=false;this._splitLength=null;this.category=false;this._autoFormatString="";this._overrideFormatString=false;e.extend(true,this,f);this.renderer.options=f;this.resetDataBounds=this.renderer.resetDataBounds;this.resetDataBounds()};e.jqplot.PyramidAxisRenderer.prototype.resetDataBounds=function(){var h=this._dataBounds;h.min=null;h.max=null;var g;for(var m=0;m<this._series.length;m++){var n=this._series[m];var o=n._plotData;for(var k=0,f=o.length;k<f;k++){if(this.name.charAt(0)==="x"){g=o[k][1];if((g!==null&&g<h.min)||h.min===null){h.min=g}if((g!==null&&g>h.max)||h.max===null){h.max=g}}else{g=o[k][0];if((g!==null&&g<h.min)||h.min===null){h.min=g}if((g!==null&&g>h.max)||h.max===null){h.max=g}}}}};e.jqplot.PyramidAxisRenderer.prototype.draw=function(f,n){if(this.show){this.renderer.createTicks.call(this,n);var m=0;var g;if(this._elem){this._elem.emptyForce();this._elem=null}this._elem=e(document.createElement("div"));this._elem.addClass("jqplot-axis jqplot-"+this.name);this._elem.css("position","absolute");if(this.name=="xaxis"||this.name=="x2axis"){this._elem.width(this._plotDimensions.width)}else{this._elem.height(this._plotDimensions.height)}this.labelOptions.axis=this.name;this._label=new this.labelRenderer(this.labelOptions);if(this._label.show){var l=this._label.draw(f,n);l.appendTo(this._elem);l=null}var k=this._ticks;var j;for(var h=0;h<k.length;h++){j=k[h];if(j.show&&j.showLabel&&(!j.isMinorTick)){this._elem.append(j.draw(f,n))}}j=null;k=null}return this._elem};var b=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];var d={};for(var c=0,a=b.length;c<a;c++){d[b[c]]=b[c]}e.jqplot.PyramidAxisRenderer.prototype.createTicks=function(D){var J=this.ticks;var M=this._dataBounds;var E;var K;var B;var G;var y;var n;var k;var h;var H;var C;var x;var L=this.min;var N=this.max;var q;var v;var m;var g;var j;var O;var A;var F;var r;var P;var z=null;var I;if(J.length){for(H=0,C=J.length;H<C;H++){q=J[H];v=new this.tickRenderer(this.tickOptions);if(e.isArray(q)){v.value=q[0];v.label=q[1];v.setTick(q[0],this.name);this._ticks.push(v)}else{if(e.isPlainObject(q)){e.extend(true,v,q);v.axis=this.name;this._ticks.push(v)}else{if(typeof q==="string"){P=H+D.defaultAxisStart}else{P=q}v.value=P;v.label=q;v.axis=this.name;this._ticks.push(v)}}}this.numberTicks=J.length;this.min=this._ticks[0].value;this.max=this._ticks[this.numberTicks-1].value;this.tickInterval=(this.max-this.min)/(this.numberTicks-1);if(this._options.tickInterval){var o=this._options.tickInterval;for(H=0;H<this.numberTicks;H++){if(H%o!==0){this._ticks[H].isMinorTick=true}}}else{E=(this.name.charAt(0)==="x")?this._plotDimensions.width:this._plotDimensions.height;r=Math.round(2+E/this.tickSpacingFactor);if(this.numberTicks>r){I=this.numberTicks-1;for(H=2;H<I;H++){if(I%H===0&&I/H<r){z=H-1;break}}if(z!==null){var p=1;for(H=1,C=this._ticks.length;H<C;H++){if(p<=z){this._ticks[H].show=false;p+=1}else{p=1}}}}}I=[];if(this.category){this._ticks[0].showGridline=false;this._ticks[0].showMark=false;for(H=this._ticks.length-1;H>0;H--){v=new this.tickRenderer(this.tickOptions);v.value=this._ticks[H-1].value+this.tickInterval/2;v.label="";v.showLabel=false;v.axis=this.name;this._ticks[H].showGridline=false;this._ticks[H].showMark=false;this._ticks.splice(H,0,v)}v=new this.tickRenderer(this.tickOptions);v.value=this._ticks[0].value-this.tickInterval/2;v.label="";v.showLabel=false;v.axis=this.name;this._ticks.unshift(v);v=new this.tickRenderer(this.tickOptions);v.value=this._ticks[this._ticks.length-1].value+this.tickInterval/2;v.label="";v.showLabel=false;v.axis=this.name;this._ticks.push(v);this.tickInterval=this.tickInterval/2;this.numberTicks=this._ticks.length;this.min=this._ticks[0].value;this.max=this._ticks[this._ticks.length-1].value}}else{if(this.name.charAt(0)==="x"){E=this._plotDimensions.width;var w=Math.max(M.max,Math.abs(M.min));var u=Math.min(M.min,-w);B=u;G=w;y=G-B;if(this.tickOptions==null||!this.tickOptions.formatString){this._overrideFormatString=true}m=30;g=Math.max(E,m+1);j=(g-m)/300;O=e.jqplot.LinearTickGenerator(B,G,j);A=B+y*(this.padMin-1);F=G-y*(this.padMax-1);if(B<A||G>F){A=B-y*(this.padMin-1);F=G+y*(this.padMax-1);O=e.jqplot.LinearTickGenerator(A,F,j)}this.min=O[0];this.max=O[1];this.numberTicks=O[2];this._autoFormatString=O[3];this.tickInterval=O[4]}else{E=this._plotDimensions.height;B=M.min;G=M.max;x=this._series[0];this._ticks=[];y=G-B;if(d[y]){y+=1;G+=1}this.max=G;this.min=B;r=Math.round(2+E/this.tickSpacingFactor);if(y+1<=r){this.numberTicks=y+1;this.tickInterval=1}else{for(var H=r;H>1;H--){if(y/(H-1)===Math.round(y/(H-1))){this.numberTicks=H;this.tickInterval=y/(H-1);break}}}}if(this._overrideFormatString&&this._autoFormatString!=""){this.tickOptions=this.tickOptions||{};this.tickOptions.formatString=this._autoFormatString}var f;for(H=0;H<this.numberTicks;H++){this.tickOptions.axis=this.name;f=this.min+this.tickInterval*H;if(this.name.charAt(0)==="x"){f=Math.abs(f)}this.tickOptions.value=this.min+this.tickInterval*H;v=new this.tickRenderer(this.tickOptions);v.label=v.prefix+v.formatter(v.formatString,f);this._ticks.push(v);if(this.name.charAt(0)==="x"&&D.axes.yMidAxis.show&&this.tickOptions.value===0){this._splitAxis=true;this._splitLength=D.axes.yMidAxis.getWidth();v=new this.tickRenderer(this.tickOptions);this._ticks.push(v);v.value=this.max/2000}}v=null}};e.jqplot.PyramidAxisRenderer.prototype.set=function(){var o=0;var j;var g=0;var n=0;var m;var l;var k;var f=(this._label==null)?false:this._label.show;if(this.show){l=this._ticks;a=l.length;for(m=0;m<a;m++){k=l[m];if(!k._breakTick&&k.show&&k.showLabel&&!k.isMinorTick){if(this.name.charAt(0)==="x"){j=k._elem.outerHeight(true)}else{j=k._elem.outerWidth(true)}if(j>o){o=j}}}if(this.name==="yMidAxis"){for(m=0;m<a;m++){k=l[m];if(k._elem){j=(o-k._elem.outerWidth(true))/2;k._elem.css("left",j)}}}k=null;l=null;if(f){g=this._label._elem.outerWidth(true);n=this._label._elem.outerHeight(true)}if(this.name==="xaxis"){o=o+n;this._elem.css({height:o+"px",left:"0px",bottom:"0px"})}else{if(this.name==="x2axis"){o=o+n;this._elem.css({height:o+"px",left:"0px",top:"0px"})}else{if(this.name==="yaxis"){o=o+g;this._elem.css({width:o+"px",left:"0px",top:"0px"});if(f&&this._label.constructor==e.jqplot.AxisLabelRenderer){this._label._elem.css("width",g+"px")}}else{if(this.name==="yMidAxis"){var j=o/2-g/2;this._elem.css({width:o+"px",top:"0px"});if(f&&this._label.constructor==e.jqplot.AxisLabelRenderer){this._label._elem.css({width:g,left:j,top:0})}}else{o=o+g;this._elem.css({width:o+"px",right:"0px",top:"0px"});if(f&&this._label.constructor==e.jqplot.AxisLabelRenderer){this._label._elem.css("width",g+"px")}}}}}}};e.jqplot.PyramidAxisRenderer.prototype.pack=function(j,g){j=j||{};g=g||this._offsets;var B=this._ticks;var v=this.max;var u=this.min;var o=g.max;var m=g.min;var r=(this._label==null)?false:this._label.show;for(var s in j){this._elem.css(s,j[s])}this._offsets=g;var k=o-m;var l=v-u;var z=this._splitLength;if(this._splitAxis){k-=this._splitLength;this.p2u=function(h){return(h-m)*l/k+u};this.u2p=function(h){if(h<=0){return(h-u)*k/l+m}else{return(h-u)*k/l+m+z}};this.series_u2p=function(h){if(h<=0){return(h-u)*k/l}else{return(h-u)*k/l+z}};this.series_p2u=function(h){return h*l/k+u}}else{this.p2u=function(h){return(h-m)*l/k+u};this.u2p=function(h){return(h-u)*k/l+m};if(this.name.charAt(0)==="x"){this.series_u2p=function(h){return(h-u)*k/l};this.series_p2u=function(h){return h*l/k+u}}else{this.series_u2p=function(h){return(h-v)*k/l};this.series_p2u=function(h){return h*l/k+v}}}if(this.show){if(this.name.charAt(0)==="x"){for(var x=0;x<B.length;x++){var q=B[x];if(q.show&&q.showLabel){var f;if(q.constructor==e.jqplot.CanvasAxisTickRenderer&&q.angle){var A=(this.name=="xaxis")?1:-1;switch(q.labelPosition){case"auto":if(A*q.angle<0){f=-q.getWidth()+q._textRenderer.height*Math.sin(-q._textRenderer.angle)/2}else{f=-q._textRenderer.height*Math.sin(q._textRenderer.angle)/2}break;case"end":f=-q.getWidth()+q._textRenderer.height*Math.sin(-q._textRenderer.angle)/2;break;case"start":f=-q._textRenderer.height*Math.sin(q._textRenderer.angle)/2;break;case"middle":f=-q.getWidth()/2+q._textRenderer.height*Math.sin(-q._textRenderer.angle)/2;break;default:f=-q.getWidth()/2+q._textRenderer.height*Math.sin(-q._textRenderer.angle)/2;break}}else{f=-q.getWidth()/2}var C=this.u2p(q.value)+f+"px";q._elem.css("left",C);q.pack()}}if(r){var n=this._label._elem.outerWidth(true);this._label._elem.css("left",m+k/2-n/2+"px");if(this.name=="xaxis"){this._label._elem.css("bottom","0px")}else{this._label._elem.css("top","0px")}this._label.pack()}}else{for(var x=0;x<B.length;x++){var q=B[x];if(q.show&&q.showLabel&&!q.isMinorTick){var f;if(q.constructor==e.jqplot.CanvasAxisTickRenderer&&q.angle){var A=(this.name=="yaxis")?1:-1;switch(q.labelPosition){case"auto":case"end":if(A*q.angle<0){f=-q._textRenderer.height*Math.cos(-q._textRenderer.angle)/2}else{f=-q.getHeight()+q._textRenderer.height*Math.cos(q._textRenderer.angle)/2}break;case"start":if(q.angle>0){f=-q._textRenderer.height*Math.cos(-q._textRenderer.angle)/2}else{f=-q.getHeight()+q._textRenderer.height*Math.cos(q._textRenderer.angle)/2}break;case"middle":f=-q.getHeight()/2;break;default:f=-q.getHeight()/2;break}}else{f=-q.getHeight()/2}var C=this.u2p(q.value)+f+"px";q._elem.css("top",C);q.pack()}}if(r){var y=this._label._elem.outerHeight(true);if(this.name!=="yMidAxis"){this._label._elem.css("top",o-k/2-y/2+"px")}if(this.name=="yaxis"){this._label._elem.css("left","0px")}else{if(this.name!=="yMidAxis"){this._label._elem.css("right","0px")}}this._label.pack()}}}B=null}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.js b/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.js new file mode 100644 index 0000000..aeea423 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.js @@ -0,0 +1,429 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + // Class: $.jqplot.CanvasGridRenderer + // The default jqPlot grid renderer, creating a grid on a canvas element. + // The renderer has no additional options beyond the <Grid> class. + $.jqplot.PyramidGridRenderer = function(){ + $.jqplot.CanvasGridRenderer.call(this); + }; + + $.jqplot.PyramidGridRenderer.prototype = new $.jqplot.CanvasGridRenderer(); + $.jqplot.PyramidGridRenderer.prototype.constructor = $.jqplot.PyramidGridRenderer; + + // called with context of Grid object + $.jqplot.CanvasGridRenderer.prototype.init = function(options) { + this._ctx; + this.plotBands = { + show: false, + color: 'rgb(230, 219, 179)', + axis: 'y', + start: null, + interval: 10 + }; + $.extend(true, this, options); + // set the shadow renderer options + var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor}; + this.renderer.shadowRenderer.init(sopts); + }; + + $.jqplot.PyramidGridRenderer.prototype.draw = function() { + this._ctx = this._elem.get(0).getContext("2d"); + var ctx = this._ctx; + var axes = this._axes; + var xp = axes.xaxis.u2p; + var yp = axes.yMidAxis.u2p; + var xnudge = axes.xaxis.max/1000.0; + var xp0 = xp(0); + var xpn = xp(xnudge); + var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis','yMidAxis']; + // Add the grid onto the grid canvas. This is the bottom most layer. + ctx.save(); + ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); + ctx.fillStyle = this.backgroundColor || this.background; + + ctx.fillRect(this._left, this._top, this._width, this._height); + + if (this.plotBands.show) { + ctx.save(); + var pb = this.plotBands; + ctx.fillStyle = pb.color; + var axis; + var x, y, w, h; + // find axis to work with + if (pb.axis.charAt(0) === 'x') { + if (axes.xaxis.show) { + axis = axes.xaxis; + } + } + else if (pb.axis.charAt(0) === 'y') { + if (axes.yaxis.show) { + axis = axes.yaxis; + } + else if (axes.y2axis.show) { + axis = axes.y2axis; + } + else if (axes.yMidAxis.show) { + axis = axes.yMidAxis; + } + } + + if (axis !== undefined) { + // draw some rectangles + var start = pb.start; + if (start === null) { + start = axis.min; + } + for (var i = start; i < axis.max; i += 2 * pb.interval) { + if (axis.name.charAt(0) === 'y') { + x = this._left; + if ((i + pb.interval) < axis.max) { + y = axis.series_u2p(i + pb.interval) + this._top; + } + else { + y = axis.series_u2p(axis.max) + this._top; + } + w = this._right - this._left; + h = axis.series_u2p(start) - axis.series_u2p(start + pb.interval); + ctx.fillRect(x, y, w, h); + } + // else { + // y = 0; + // x = axis.series_u2p(i); + // h = this._height; + // w = axis.series_u2p(start + pb.interval) - axis.series_u2p(start); + // } + + } + } + ctx.restore(); + } + + ctx.save(); + ctx.lineJoin = 'miter'; + ctx.lineCap = 'butt'; + ctx.lineWidth = this.gridLineWidth; + ctx.strokeStyle = this.gridLineColor; + var b, e, s, m; + for (var i=5; i>0; i--) { + var name = ax[i-1]; + var axis = axes[name]; + var ticks = axis._ticks; + var numticks = ticks.length; + if (axis.show) { + if (axis.drawBaseline) { + var bopts = {}; + if (axis.baselineWidth !== null) { + bopts.lineWidth = axis.baselineWidth; + } + if (axis.baselineColor !== null) { + bopts.strokeStyle = axis.baselineColor; + } + switch (name) { + case 'xaxis': + if (axes.yMidAxis.show) { + drawLine (this._left, this._bottom, xp0, this._bottom, bopts); + drawLine (xpn, this._bottom, this._right, this._bottom, bopts); + } + else { + drawLine (this._left, this._bottom, this._right, this._bottom, bopts); + } + break; + case 'yaxis': + drawLine (this._left, this._bottom, this._left, this._top, bopts); + break; + case 'yMidAxis': + drawLine(xp0, this._bottom, xp0, this._top, bopts); + drawLine(xpn, this._bottom, xpn, this._top, bopts); + break; + case 'x2axis': + if (axes.yMidAxis.show) { + drawLine (this._left, this._top, xp0, this._top, bopts); + drawLine (xpn, this._top, this._right, this._top, bopts); + } + else { + drawLine (this._left, this._bottom, this._right, this._bottom, bopts); + } + break; + case 'y2axis': + drawLine (this._right, this._bottom, this._right, this._top, bopts); + break; + + } + } + for (var j=numticks; j>0; j--) { + var t = ticks[j-1]; + if (t.show) { + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (name) { + case 'xaxis': + // draw the grid line if we should + if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { + drawLine(pos, this._top, pos, this._bottom); + } + + // draw the mark + if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._bottom; + e = this._bottom+s; + break; + case 'inside': + b = this._bottom-s; + e = this._bottom; + break; + case 'cross': + b = this._bottom-s; + e = this._bottom+s; + break; + default: + b = this._bottom; + e = this._bottom+s; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); + } + // draw the line + drawLine(pos, b, pos, e); + } + break; + case 'yaxis': + // draw the grid line + if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { + drawLine(this._right, pos, this._left, pos); + } + + // draw the mark + if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._left-s; + e = this._left; + break; + case 'inside': + b = this._left; + e = this._left+s; + break; + case 'cross': + b = this._left-s; + e = this._left+s; + break; + default: + b = this._left-s; + e = this._left; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + break; + case 'yMidAxis': + // draw the grid line + if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { + drawLine(this._left, pos, xp0, pos); + drawLine(xpn, pos, this._right, pos); + } + // draw the mark + if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + + b = xp0; + e = xp0 + s; + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + + b = xpn - s; + e = xpn; + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + break; + case 'x2axis': + // draw the grid line + if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { + drawLine(pos, this._bottom, pos, this._top); + } + + // draw the mark + if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._top-s; + e = this._top; + break; + case 'inside': + b = this._top; + e = this._top+s; + break; + case 'cross': + b = this._top-s; + e = this._top+s; + break; + default: + b = this._top-s; + e = this._top; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); + } + drawLine(pos, b, pos, e); + } + break; + case 'y2axis': + // draw the grid line + if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { + drawLine(this._left, pos, this._right, pos); + } + + // draw the mark + if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { + s = t.markSize; + m = t.mark; + var pos = Math.round(axis.u2p(t.value)) + 0.5; + switch (m) { + case 'outside': + b = this._right; + e = this._right+s; + break; + case 'inside': + b = this._right-s; + e = this._right; + break; + case 'cross': + b = this._right-s; + e = this._right+s; + break; + default: + b = this._right; + e = this._right+s; + break; + } + // draw the shadow + if (this.shadow) { + this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); + } + drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); + } + break; + default: + break; + } + } + } + t = null; + } + axis = null; + ticks = null; + } + + ctx.restore(); + + function drawLine(bx, by, ex, ey, opts) { + ctx.save(); + opts = opts || {}; + if (opts.lineWidth == null || opts.lineWidth != 0){ + $.extend(true, ctx, opts); + ctx.beginPath(); + ctx.moveTo(bx, by); + ctx.lineTo(ex, ey); + ctx.stroke(); + } + ctx.restore(); + } + + if (this.shadow) { + if (axes.yMidAxis.show) { + var points = [[this._left, this._bottom], [xp0, this._bottom]]; + this.renderer.shadowRenderer.draw(ctx, points); + var points = [[xpn, this._bottom], [this._right, this._bottom], [this._right, this._top]]; + this.renderer.shadowRenderer.draw(ctx, points); + var points = [[xp0, this._bottom], [xp0, this._top]]; + this.renderer.shadowRenderer.draw(ctx, points); + } + else { + var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; + this.renderer.shadowRenderer.draw(ctx, points); + } + } + // Now draw border around grid. Use axis border definitions. start at + // upper left and go clockwise. + if (this.borderWidth != 0 && this.drawBorder) { + if (axes.yMidAxis.show) { + drawLine (this._left, this._top, xp0, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); + drawLine (xpn, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); + drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); + drawLine (this._right, this._bottom, xpn, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); + drawLine (xp0, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); + drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); + drawLine (xp0, this._bottom, xp0, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); + drawLine (xpn, this._bottom, xpn, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); + } + else { + drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); + drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); + drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); + drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); + } + } + // ctx.lineWidth = this.borderWidth; + // ctx.strokeStyle = this.borderColor; + // ctx.strokeRect(this._left, this._top, this._width, this._height); + + ctx.restore(); + ctx = null; + axes = null; + }; +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.min.js new file mode 100644 index 0000000..2576991 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidGridRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(a){a.jqplot.PyramidGridRenderer=function(){a.jqplot.CanvasGridRenderer.call(this)};a.jqplot.PyramidGridRenderer.prototype=new a.jqplot.CanvasGridRenderer();a.jqplot.PyramidGridRenderer.prototype.constructor=a.jqplot.PyramidGridRenderer;a.jqplot.CanvasGridRenderer.prototype.init=function(c){this._ctx;this.plotBands={show:false,color:"rgb(230, 219, 179)",axis:"y",start:null,interval:10};a.extend(true,this,c);var b={lineJoin:"miter",lineCap:"round",fill:false,isarc:false,angle:this.shadowAngle,offset:this.shadowOffset,alpha:this.shadowAlpha,depth:this.shadowDepth,lineWidth:this.shadowWidth,closePath:false,strokeStyle:this.shadowColor};this.renderer.shadowRenderer.init(b)};a.jqplot.PyramidGridRenderer.prototype.draw=function(){this._ctx=this._elem.get(0).getContext("2d");var D=this._ctx;var G=this._axes;var q=G.xaxis.u2p;var J=G.yMidAxis.u2p;var l=G.xaxis.max/1000;var u=q(0);var f=q(l);var r=["xaxis","yaxis","x2axis","y2axis","yMidAxis"];D.save();D.clearRect(0,0,this._plotDimensions.width,this._plotDimensions.height);D.fillStyle=this.backgroundColor||this.background;D.fillRect(this._left,this._top,this._width,this._height);if(this.plotBands.show){D.save();var c=this.plotBands;D.fillStyle=c.color;var d;var o,n,p,I;if(c.axis.charAt(0)==="x"){if(G.xaxis.show){d=G.xaxis}}else{if(c.axis.charAt(0)==="y"){if(G.yaxis.show){d=G.yaxis}else{if(G.y2axis.show){d=G.y2axis}else{if(G.yMidAxis.show){d=G.yMidAxis}}}}}if(d!==undefined){var g=c.start;if(g===null){g=d.min}for(var H=g;H<d.max;H+=2*c.interval){if(d.name.charAt(0)==="y"){o=this._left;if((H+c.interval)<d.max){n=d.series_u2p(H+c.interval)+this._top}else{n=d.series_u2p(d.max)+this._top}p=this._right-this._left;I=d.series_u2p(g)-d.series_u2p(g+c.interval);D.fillRect(o,n,p,I)}}}D.restore()}D.save();D.lineJoin="miter";D.lineCap="butt";D.lineWidth=this.gridLineWidth;D.strokeStyle=this.gridLineColor;var L,K,A,C;for(var H=5;H>0;H--){var O=r[H-1];var d=G[O];var M=d._ticks;var B=M.length;if(d.show){if(d.drawBaseline){var N={};if(d.baselineWidth!==null){N.lineWidth=d.baselineWidth}if(d.baselineColor!==null){N.strokeStyle=d.baselineColor}switch(O){case"xaxis":if(G.yMidAxis.show){z(this._left,this._bottom,u,this._bottom,N);z(f,this._bottom,this._right,this._bottom,N)}else{z(this._left,this._bottom,this._right,this._bottom,N)}break;case"yaxis":z(this._left,this._bottom,this._left,this._top,N);break;case"yMidAxis":z(u,this._bottom,u,this._top,N);z(f,this._bottom,f,this._top,N);break;case"x2axis":if(G.yMidAxis.show){z(this._left,this._top,u,this._top,N);z(f,this._top,this._right,this._top,N)}else{z(this._left,this._bottom,this._right,this._bottom,N)}break;case"y2axis":z(this._right,this._bottom,this._right,this._top,N);break}}for(var E=B;E>0;E--){var v=M[E-1];if(v.show){var k=Math.round(d.u2p(v.value))+0.5;switch(O){case"xaxis":if(v.showGridline&&this.drawGridlines&&(!v.isMinorTick||d.showMinorTicks)){z(k,this._top,k,this._bottom)}if(v.showMark&&v.mark&&(!v.isMinorTick||d.showMinorTicks)){A=v.markSize;C=v.mark;var k=Math.round(d.u2p(v.value))+0.5;switch(C){case"outside":L=this._bottom;K=this._bottom+A;break;case"inside":L=this._bottom-A;K=this._bottom;break;case"cross":L=this._bottom-A;K=this._bottom+A;break;default:L=this._bottom;K=this._bottom+A;break}if(this.shadow){this.renderer.shadowRenderer.draw(D,[[k,L],[k,K]],{lineCap:"butt",lineWidth:this.gridLineWidth,offset:this.gridLineWidth*0.75,depth:2,fill:false,closePath:false})}z(k,L,k,K)}break;case"yaxis":if(v.showGridline&&this.drawGridlines&&(!v.isMinorTick||d.showMinorTicks)){z(this._right,k,this._left,k)}if(v.showMark&&v.mark&&(!v.isMinorTick||d.showMinorTicks)){A=v.markSize;C=v.mark;var k=Math.round(d.u2p(v.value))+0.5;switch(C){case"outside":L=this._left-A;K=this._left;break;case"inside":L=this._left;K=this._left+A;break;case"cross":L=this._left-A;K=this._left+A;break;default:L=this._left-A;K=this._left;break}if(this.shadow){this.renderer.shadowRenderer.draw(D,[[L,k],[K,k]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}z(L,k,K,k,{strokeStyle:d.borderColor})}break;case"yMidAxis":if(v.showGridline&&this.drawGridlines&&(!v.isMinorTick||d.showMinorTicks)){z(this._left,k,u,k);z(f,k,this._right,k)}if(v.showMark&&v.mark&&(!v.isMinorTick||d.showMinorTicks)){A=v.markSize;C=v.mark;var k=Math.round(d.u2p(v.value))+0.5;L=u;K=u+A;if(this.shadow){this.renderer.shadowRenderer.draw(D,[[L,k],[K,k]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}z(L,k,K,k,{strokeStyle:d.borderColor});L=f-A;K=f;if(this.shadow){this.renderer.shadowRenderer.draw(D,[[L,k],[K,k]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}z(L,k,K,k,{strokeStyle:d.borderColor})}break;case"x2axis":if(v.showGridline&&this.drawGridlines&&(!v.isMinorTick||d.showMinorTicks)){z(k,this._bottom,k,this._top)}if(v.showMark&&v.mark&&(!v.isMinorTick||d.showMinorTicks)){A=v.markSize;C=v.mark;var k=Math.round(d.u2p(v.value))+0.5;switch(C){case"outside":L=this._top-A;K=this._top;break;case"inside":L=this._top;K=this._top+A;break;case"cross":L=this._top-A;K=this._top+A;break;default:L=this._top-A;K=this._top;break}if(this.shadow){this.renderer.shadowRenderer.draw(D,[[k,L],[k,K]],{lineCap:"butt",lineWidth:this.gridLineWidth,offset:this.gridLineWidth*0.75,depth:2,fill:false,closePath:false})}z(k,L,k,K)}break;case"y2axis":if(v.showGridline&&this.drawGridlines&&(!v.isMinorTick||d.showMinorTicks)){z(this._left,k,this._right,k)}if(v.showMark&&v.mark&&(!v.isMinorTick||d.showMinorTicks)){A=v.markSize;C=v.mark;var k=Math.round(d.u2p(v.value))+0.5;switch(C){case"outside":L=this._right;K=this._right+A;break;case"inside":L=this._right-A;K=this._right;break;case"cross":L=this._right-A;K=this._right+A;break;default:L=this._right;K=this._right+A;break}if(this.shadow){this.renderer.shadowRenderer.draw(D,[[L,k],[K,k]],{lineCap:"butt",lineWidth:this.gridLineWidth*1.5,offset:this.gridLineWidth*0.75,fill:false,closePath:false})}z(L,k,K,k,{strokeStyle:d.borderColor})}break;default:break}}}v=null}d=null;M=null}D.restore();function z(j,i,e,b,h){D.save();h=h||{};if(h.lineWidth==null||h.lineWidth!=0){a.extend(true,D,h);D.beginPath();D.moveTo(j,i);D.lineTo(e,b);D.stroke()}D.restore()}if(this.shadow){if(G.yMidAxis.show){var F=[[this._left,this._bottom],[u,this._bottom]];this.renderer.shadowRenderer.draw(D,F);var F=[[f,this._bottom],[this._right,this._bottom],[this._right,this._top]];this.renderer.shadowRenderer.draw(D,F);var F=[[u,this._bottom],[u,this._top]];this.renderer.shadowRenderer.draw(D,F)}else{var F=[[this._left,this._bottom],[this._right,this._bottom],[this._right,this._top]];this.renderer.shadowRenderer.draw(D,F)}}if(this.borderWidth!=0&&this.drawBorder){if(G.yMidAxis.show){z(this._left,this._top,u,this._top,{lineCap:"round",strokeStyle:G.x2axis.borderColor,lineWidth:G.x2axis.borderWidth});z(f,this._top,this._right,this._top,{lineCap:"round",strokeStyle:G.x2axis.borderColor,lineWidth:G.x2axis.borderWidth});z(this._right,this._top,this._right,this._bottom,{lineCap:"round",strokeStyle:G.y2axis.borderColor,lineWidth:G.y2axis.borderWidth});z(this._right,this._bottom,f,this._bottom,{lineCap:"round",strokeStyle:G.xaxis.borderColor,lineWidth:G.xaxis.borderWidth});z(u,this._bottom,this._left,this._bottom,{lineCap:"round",strokeStyle:G.xaxis.borderColor,lineWidth:G.xaxis.borderWidth});z(this._left,this._bottom,this._left,this._top,{lineCap:"round",strokeStyle:G.yaxis.borderColor,lineWidth:G.yaxis.borderWidth});z(u,this._bottom,u,this._top,{lineCap:"round",strokeStyle:G.yaxis.borderColor,lineWidth:G.yaxis.borderWidth});z(f,this._bottom,f,this._top,{lineCap:"round",strokeStyle:G.yaxis.borderColor,lineWidth:G.yaxis.borderWidth})}else{z(this._left,this._top,this._right,this._top,{lineCap:"round",strokeStyle:G.x2axis.borderColor,lineWidth:G.x2axis.borderWidth});z(this._right,this._top,this._right,this._bottom,{lineCap:"round",strokeStyle:G.y2axis.borderColor,lineWidth:G.y2axis.borderWidth});z(this._right,this._bottom,this._left,this._bottom,{lineCap:"round",strokeStyle:G.xaxis.borderColor,lineWidth:G.xaxis.borderWidth});z(this._left,this._bottom,this._left,this._top,{lineCap:"round",strokeStyle:G.yaxis.borderColor,lineWidth:G.yaxis.borderWidth})}}D.restore();D=null;G=null}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.js b/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.js new file mode 100644 index 0000000..f230d9f --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.js @@ -0,0 +1,514 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + // Need to ensure pyramid axis and grid renderers are loaded. + // You should load these with script tags in the html head, that is more efficient + // as the browser will cache the request. + // Note, have to block with synchronous request in order to execute bar renderer code. + if ($.jqplot.PyramidAxisRenderer === undefined) { + $.ajax({ + url: $.jqplot.pluginLocation + 'jqplot.pyramidAxisRenderer.js', + dataType: "script", + async: false + }); + } + + if ($.jqplot.PyramidGridRenderer === undefined) { + $.ajax({ + url: $.jqplot.pluginLocation + 'jqplot.pyramidGridRenderer.js', + dataType: "script", + async: false + }); + } + + $.jqplot.PyramidRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.PyramidRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.PyramidRenderer.prototype.constructor = $.jqplot.PyramidRenderer; + + // called with scope of a series + $.jqplot.PyramidRenderer.prototype.init = function(options, plot) { + options = options || {}; + this._type = 'pyramid'; + // Group: Properties + // + // prop: barPadding + this.barPadding = 10; + this.barWidth = null; + // prop: fill + // True to fill the bars. + this.fill = true; + // prop: highlightMouseOver + // True to highlight slice when moused over. + // This must be false to enable highlightMouseDown to highlight when clicking on a slice. + this.highlightMouseOver = true; + // prop: highlightMouseDown + // True to highlight when a mouse button is pressed over a slice. + // This will be disabled if highlightMouseOver is true. + this.highlightMouseDown = false; + // prop: highlightColors + // an array of colors to use when highlighting a slice. + this.highlightColors = []; + // prop highlightThreshold + // Expand the highlightable region in the x direction. + // E.g. a value of 3 will highlight a bar when the mouse is + // within 3 pixels of the bar in the x direction. + this.highlightThreshold = 2; + // prop: synchronizeHighlight + // Index of another series to highlight when this series is highlighted. + // null or false to not synchronize. + this.synchronizeHighlight = false; + // prop: offsetBars + // False will center bars on their y value. + // True will push bars up by 1/2 bar width to fill between their y values. + // If true, there needs to be 1 more tick than there are bars. + this.offsetBars = false; + + // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver + if (options.highlightMouseDown && options.highlightMouseOver == null) { + options.highlightMouseOver = false; + } + + this.side = 'right'; + + $.extend(true, this, options); + + // if (this.fill === false) { + // this.shadow = false; + // } + + if (this.side === 'left') { + this._highlightThreshold = [[-this.highlightThreshold, 0], [-this.highlightThreshold, 0], [0,0], [0,0]]; + } + + else { + this._highlightThreshold = [[0,0], [0,0], [this.highlightThreshold, 0], [this.highlightThreshold, 0]]; + } + + this.renderer.options = options; + // index of the currenty highlighted point, if any + this._highlightedPoint = null; + // Array of actual data colors used for each data point. + this._dataColors = []; + this._barPoints = []; + this.fillAxis = 'y'; + this._primaryAxis = '_yaxis'; + this._xnudge = 0; + + // set the shape renderer options + var opts = {lineJoin:'miter', lineCap:'butt', fill:this.fill, fillRect:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill, lineWidth: this.lineWidth}; + this.renderer.shapeRenderer.init(opts); + // set the shadow renderer options + var shadow_offset = options.shadowOffset; + // set the shadow renderer options + if (shadow_offset == null) { + // scale the shadowOffset to the width of the line. + if (this.lineWidth > 2.5) { + shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); + // var shadow_offset = this.shadowOffset; + } + // for skinny lines, don't make such a big shadow. + else { + shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; + } + } + var sopts = {lineJoin:'miter', lineCap:'butt', fill:this.fill, fillRect:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill, lineWidth: this.lineWidth}; + this.renderer.shadowRenderer.init(sopts); + + plot.postDrawHooks.addOnce(postPlotDraw); + plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); + + // if this is the left side of pyramid, set y values to negative. + if (this.side === 'left') { + for (var i=0, l=this.data.length; i<l; i++) { + this.data[i][1] = -Math.abs(this.data[i][1]); + } + } + }; + + // setGridData + // converts the user data values to grid coordinates and stores them + // in the gridData array. + // Called with scope of a series. + $.jqplot.PyramidRenderer.prototype.setGridData = function(plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var data = this._plotData; + var pdata = this._prevPlotData; + this.gridData = []; + this._prevGridData = []; + var l = data.length; + var adjust = false; + var i; + + // if any data values are < 0, consider this a negative series + for (i = 0; i < l; i++) { + if (data[i][1] < 0) { + this.side = 'left'; + } + } + + if (this._yaxis.name === 'yMidAxis' && this.side === 'right') { + this._xnudge = this._xaxis.max/2000.0; + adjust = true; + } + + for (i = 0; i < l; i++) { + // if not a line series or if no nulls in data, push the converted point onto the array. + if (data[i][0] != null && data[i][1] != null) { + this.gridData.push([xp(data[i][1]), yp(data[i][0])]); + } + // else if there is a null, preserve it. + else if (data[i][0] == null) { + this.gridData.push([xp(data[i][1]), null]); + } + else if (data[i][1] == null) { + this.gridData.push(null, [yp(data[i][0])]); + } + // finally, adjust x grid data if have to + if (data[i][1] === 0 && adjust) { + this.gridData[i][0] = xp(this._xnudge); + } + } + }; + + // makeGridData + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.PyramidRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var gd = []; + var l = data.length; + var adjust = false; + var i; + + // if any data values are < 0, consider this a negative series + for (i = 0; i < l; i++) { + if (data[i][1] < 0) { + this.side = 'left'; + } + } + + if (this._yaxis.name === 'yMidAxis' && this.side === 'right') { + this._xnudge = this._xaxis.max/2000.0; + adjust = true; + } + + for (i = 0; i < l; i++) { + // if not a line series or if no nulls in data, push the converted point onto the array. + if (data[i][0] != null && data[i][1] != null) { + gd.push([xp(data[i][1]), yp(data[i][0])]); + } + // else if there is a null, preserve it. + else if (data[i][0] == null) { + gd.push([xp(data[i][1]), null]); + } + else if (data[i][1] == null) { + gd.push([null, yp(data[i][0])]); + } + // finally, adjust x grid data if have to + if (data[i][1] === 0 && adjust) { + gd[i][0] = xp(this._xnudge); + } + } + + return gd; + }; + + $.jqplot.PyramidRenderer.prototype.setBarWidth = function() { + // need to know how many data values we have on the approprate axis and figure it out. + var i; + var nvals = 0; + var nseries = 0; + var paxis = this[this._primaryAxis]; + var s, series, pos; + nvals = paxis.max - paxis.min; + var nticks = paxis.numberTicks; + var nbins = (nticks-1)/2; + // so, now we have total number of axis values. + var temp = (this.barPadding === 0) ? 1.0 : 0; + if (paxis.name == 'xaxis' || paxis.name == 'x2axis') { + this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding + temp; + } + else { + if (this.fill) { + this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding + temp; + } + else { + this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals; + } + } + }; + + $.jqplot.PyramidRenderer.prototype.draw = function(ctx, gridData, options) { + var i; + // Ughhh, have to make a copy of options b/c it may be modified later. + var opts = $.extend({}, options); + var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; + var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; + var fill = (opts.fill != undefined) ? opts.fill : this.fill; + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var pointx, pointy; + // clear out data colors. + this._dataColors = []; + this._barPoints = []; + + if (this.renderer.options.barWidth == null) { + this.renderer.setBarWidth.call(this); + } + + // var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); + // var nvals = temp[0]; + // var nseries = temp[1]; + // var pos = temp[2]; + var points = [], + w, + h; + + // this._barNudge = 0; + + if (showLine) { + var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors); + var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors); + var negativeColor = negativeColors.get(this.index); + if (! this.useNegativeColors) { + negativeColor = opts.fillStyle; + } + var positiveColor = opts.fillStyle; + var base; + var xstart = this._xaxis.series_u2p(this._xnudge); + var ystart = this._yaxis.series_u2p(this._yaxis.min); + var yend = this._yaxis.series_u2p(this._yaxis.max); + var bw = this.barWidth; + var bw2 = bw/2.0; + var points = []; + var yadj = this.offsetBars ? bw2 : 0; + + for (var i=0, l=gridData.length; i<l; i++) { + if (this.data[i][0] == null) { + continue; + } + base = gridData[i][1]; + // not stacked and first series in stack + + if (this._plotData[i][1] < 0) { + if (this.varyBarColor && !this._stack) { + if (this.useNegativeColors) { + opts.fillStyle = negativeColors.next(); + } + else { + opts.fillStyle = positiveColors.next(); + } + } + } + else { + if (this.varyBarColor && !this._stack) { + opts.fillStyle = positiveColors.next(); + } + else { + opts.fillStyle = positiveColor; + } + } + + if (this.fill) { + + if (this._plotData[i][1] >= 0) { + // xstart = this._xaxis.series_u2p(this._xnudge); + w = gridData[i][0] - xstart; + h = this.barWidth; + points = [xstart, base - bw2 - yadj, w, h]; + } + else { + // xstart = this._xaxis.series_u2p(0); + w = xstart - gridData[i][0]; + h = this.barWidth; + points = [gridData[i][0], base - bw2 - yadj, w, h]; + } + + this._barPoints.push([[points[0], points[1] + h], [points[0], points[1]], [points[0] + w, points[1]], [points[0] + w, points[1] + h]]); + + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, points); + } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); + this.renderer.shapeRenderer.draw(ctx, points, opts); + } + + else { + if (i === 0) { + points =[[xstart, ystart], [gridData[i][0], ystart], [gridData[i][0], gridData[i][1] - bw2 - yadj]]; + } + + else if (i < l-1) { + points = points.concat([[gridData[i-1][0], gridData[i-1][1] - bw2 - yadj], [gridData[i][0], gridData[i][1] + bw2 - yadj], [gridData[i][0], gridData[i][1] - bw2 - yadj]]); + } + + // finally, draw the line + else { + points = points.concat([[gridData[i-1][0], gridData[i-1][1] - bw2 - yadj], [gridData[i][0], gridData[i][1] + bw2 - yadj], [gridData[i][0], yend], [xstart, yend]]); + + if (shadow) { + this.renderer.shadowRenderer.draw(ctx, points); + } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); + this.renderer.shapeRenderer.draw(ctx, points, opts); + } + } + } + } + + if (this.highlightColors.length == 0) { + this.highlightColors = $.jqplot.computeHighlightColors(this._dataColors); + } + + else if (typeof(this.highlightColors) == 'string') { + this.highlightColors = []; + for (var i=0; i<this._dataColors.length; i++) { + this.highlightColors.push(this.highlightColors); + } + } + + }; + + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = options.axesDefaults || {}; + options.grid = options.grid || {}; + options.legend = options.legend || {}; + options.seriesDefaults = options.seriesDefaults || {}; + // only set these if there is a pie series + var setopts = false; + if (options.seriesDefaults.renderer === $.jqplot.PyramidRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer === $.jqplot.PyramidRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.PyramidAxisRenderer; + options.grid.renderer = $.jqplot.PyramidGridRenderer; + options.seriesDefaults.pointLabels = {show: false}; + } + } + + // called within context of plot + // create a canvas which we can draw on. + // insert it before the eventCanvas, so eventCanvas will still capture events. + function postPlotDraw() { + // Memory Leaks patch + if (this.plugins.pyramidRenderer && this.plugins.pyramidRenderer.highlightCanvas) { + + this.plugins.pyramidRenderer.highlightCanvas.resetCanvas(); + this.plugins.pyramidRenderer.highlightCanvas = null; + } + + this.plugins.pyramidRenderer = {highlightedSeriesIndex:null}; + this.plugins.pyramidRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); + + this.eventCanvas._elem.before(this.plugins.pyramidRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pyramidRenderer-highlight-canvas', this._plotDimensions, this)); + this.plugins.pyramidRenderer.highlightCanvas.setContext(); + this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); + } + + function highlight (plot, sidx, pidx, points) { + var s = plot.series[sidx]; + var canvas = plot.plugins.pyramidRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); + s._highlightedPoint = pidx; + plot.plugins.pyramidRenderer.highlightedSeriesIndex = sidx; + var opts = {fillStyle: s.highlightColors[pidx], fillRect: false}; + s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); + if (s.synchronizeHighlight !== false && plot.series.length >= s.synchronizeHighlight && s.synchronizeHighlight !== sidx) { + s = plot.series[s.synchronizeHighlight]; + opts = {fillStyle: s.highlightColors[pidx], fillRect: false}; + s.renderer.shapeRenderer.draw(canvas._ctx, s._barPoints[pidx], opts); + } + canvas = null; + } + + function unhighlight (plot) { + var canvas = plot.plugins.pyramidRenderer.highlightCanvas; + canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); + for (var i=0; i<plot.series.length; i++) { + plot.series[i]._highlightedPoint = null; + } + plot.plugins.pyramidRenderer.highlightedSeriesIndex = null; + plot.target.trigger('jqplotDataUnhighlight'); + canvas = null; + } + + + function handleMove(ev, gridpos, datapos, neighbor, plot) { + if (neighbor) { + var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; + var evt1 = jQuery.Event('jqplotDataMouseOver'); + evt1.pageX = ev.pageX; + evt1.pageY = ev.pageY; + plot.target.trigger(evt1, ins); + if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pyramidRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { + var evt = jQuery.Event('jqplotDataHighlight'); + evt.which = ev.which; + evt.pageX = ev.pageX; + evt.pageY = ev.pageY; + plot.target.trigger(evt, ins); + highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); + } + } + else if (neighbor == null) { + unhighlight (plot); + } + } + + // Have to add hook here, becuase it needs called before series is inited. + $.jqplot.preInitHooks.push(preInit); + + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.min.js b/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.min.js new file mode 100644 index 0000000..9266e22 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.pyramidRenderer.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(c){if(c.jqplot.PyramidAxisRenderer===undefined){c.ajax({url:c.jqplot.pluginLocation+"jqplot.pyramidAxisRenderer.js",dataType:"script",async:false})}if(c.jqplot.PyramidGridRenderer===undefined){c.ajax({url:c.jqplot.pluginLocation+"jqplot.pyramidGridRenderer.js",dataType:"script",async:false})}c.jqplot.PyramidRenderer=function(){c.jqplot.LineRenderer.call(this)};c.jqplot.PyramidRenderer.prototype=new c.jqplot.LineRenderer();c.jqplot.PyramidRenderer.prototype.constructor=c.jqplot.PyramidRenderer;c.jqplot.PyramidRenderer.prototype.init=function(j,o){j=j||{};this._type="pyramid";this.barPadding=10;this.barWidth=null;this.fill=true;this.highlightMouseOver=true;this.highlightMouseDown=false;this.highlightColors=[];this.highlightThreshold=2;this.synchronizeHighlight=false;this.offsetBars=false;if(j.highlightMouseDown&&j.highlightMouseOver==null){j.highlightMouseOver=false}this.side="right";c.extend(true,this,j);if(this.side==="left"){this._highlightThreshold=[[-this.highlightThreshold,0],[-this.highlightThreshold,0],[0,0],[0,0]]}else{this._highlightThreshold=[[0,0],[0,0],[this.highlightThreshold,0],[this.highlightThreshold,0]]}this.renderer.options=j;this._highlightedPoint=null;this._dataColors=[];this._barPoints=[];this.fillAxis="y";this._primaryAxis="_yaxis";this._xnudge=0;var n={lineJoin:"miter",lineCap:"butt",fill:this.fill,fillRect:this.fill,isarc:false,strokeStyle:this.color,fillStyle:this.color,closePath:this.fill,lineWidth:this.lineWidth};this.renderer.shapeRenderer.init(n);var m=j.shadowOffset;if(m==null){if(this.lineWidth>2.5){m=1.25*(1+(Math.atan((this.lineWidth/2.5))/0.785398163-1)*0.6)}else{m=1.25*Math.atan((this.lineWidth/2.5))/0.785398163}}var h={lineJoin:"miter",lineCap:"butt",fill:this.fill,fillRect:this.fill,isarc:false,angle:this.shadowAngle,offset:m,alpha:this.shadowAlpha,depth:this.shadowDepth,closePath:this.fill,lineWidth:this.lineWidth};this.renderer.shadowRenderer.init(h);o.postDrawHooks.addOnce(f);o.eventListenerHooks.addOnce("jqplotMouseMove",e);if(this.side==="left"){for(var k=0,g=this.data.length;k<g;k++){this.data[k][1]=-Math.abs(this.data[k][1])}}};c.jqplot.PyramidRenderer.prototype.setGridData=function(p){var j=this._xaxis.series_u2p;var o=this._yaxis.series_u2p;var k=this._plotData;var n=this._prevPlotData;this.gridData=[];this._prevGridData=[];var g=k.length;var m=false;var h;for(h=0;h<g;h++){if(k[h][1]<0){this.side="left"}}if(this._yaxis.name==="yMidAxis"&&this.side==="right"){this._xnudge=this._xaxis.max/2000;m=true}for(h=0;h<g;h++){if(k[h][0]!=null&&k[h][1]!=null){this.gridData.push([j(k[h][1]),o(k[h][0])])}else{if(k[h][0]==null){this.gridData.push([j(k[h][1]),null])}else{if(k[h][1]==null){this.gridData.push(null,[o(k[h][0])])}}}if(k[h][1]===0&&m){this.gridData[h][0]=j(this._xnudge)}}};c.jqplot.PyramidRenderer.prototype.makeGridData=function(m,p){var k=this._xaxis.series_u2p;var o=this._yaxis.series_u2p;var j=[];var g=m.length;var n=false;var h;for(h=0;h<g;h++){if(m[h][1]<0){this.side="left"}}if(this._yaxis.name==="yMidAxis"&&this.side==="right"){this._xnudge=this._xaxis.max/2000;n=true}for(h=0;h<g;h++){if(m[h][0]!=null&&m[h][1]!=null){j.push([k(m[h][1]),o(m[h][0])])}else{if(m[h][0]==null){j.push([k(m[h][1]),null])}else{if(m[h][1]==null){j.push([null,o(m[h][0])])}}}if(m[h][1]===0&&n){j[h][0]=k(this._xnudge)}}return j};c.jqplot.PyramidRenderer.prototype.setBarWidth=function(){var k;var g=0;var h=0;var m=this[this._primaryAxis];var q,l,o;g=m.max-m.min;var n=m.numberTicks;var j=(n-1)/2;var p=(this.barPadding===0)?1:0;if(m.name=="xaxis"||m.name=="x2axis"){this.barWidth=(m._offsets.max-m._offsets.min)/g-this.barPadding+p}else{if(this.fill){this.barWidth=(m._offsets.min-m._offsets.max)/g-this.barPadding+p}else{this.barWidth=(m._offsets.min-m._offsets.max)/g}}};c.jqplot.PyramidRenderer.prototype.draw=function(B,I,k){var E;var u=c.extend({},k);var p=(u.shadow!=undefined)?u.shadow:this.shadow;var K=(u.showLine!=undefined)?u.showLine:this.showLine;var C=(u.fill!=undefined)?u.fill:this.fill;var t=this._xaxis.series_u2p;var G=this._yaxis.series_u2p;var z,x;this._dataColors=[];this._barPoints=[];if(this.renderer.options.barWidth==null){this.renderer.setBarWidth.call(this)}var D=[],s,F;if(K){var q=new c.jqplot.ColorGenerator(this.negativeSeriesColors);var v=new c.jqplot.ColorGenerator(this.seriesColors);var J=q.get(this.index);if(!this.useNegativeColors){J=u.fillStyle}var o=u.fillStyle;var n;var L=this._xaxis.series_u2p(this._xnudge);var j=this._yaxis.series_u2p(this._yaxis.min);var m=this._yaxis.series_u2p(this._yaxis.max);var H=this.barWidth;var y=H/2;var D=[];var r=this.offsetBars?y:0;for(var E=0,A=I.length;E<A;E++){if(this.data[E][0]==null){continue}n=I[E][1];if(this._plotData[E][1]<0){if(this.varyBarColor&&!this._stack){if(this.useNegativeColors){u.fillStyle=q.next()}else{u.fillStyle=v.next()}}}else{if(this.varyBarColor&&!this._stack){u.fillStyle=v.next()}else{u.fillStyle=o}}if(this.fill){if(this._plotData[E][1]>=0){s=I[E][0]-L;F=this.barWidth;D=[L,n-y-r,s,F]}else{s=L-I[E][0];F=this.barWidth;D=[I[E][0],n-y-r,s,F]}this._barPoints.push([[D[0],D[1]+F],[D[0],D[1]],[D[0]+s,D[1]],[D[0]+s,D[1]+F]]);if(p){this.renderer.shadowRenderer.draw(B,D)}var g=u.fillStyle||this.color;this._dataColors.push(g);this.renderer.shapeRenderer.draw(B,D,u)}else{if(E===0){D=[[L,j],[I[E][0],j],[I[E][0],I[E][1]-y-r]]}else{if(E<A-1){D=D.concat([[I[E-1][0],I[E-1][1]-y-r],[I[E][0],I[E][1]+y-r],[I[E][0],I[E][1]-y-r]])}else{D=D.concat([[I[E-1][0],I[E-1][1]-y-r],[I[E][0],I[E][1]+y-r],[I[E][0],m],[L,m]]);if(p){this.renderer.shadowRenderer.draw(B,D)}var g=u.fillStyle||this.color;this._dataColors.push(g);this.renderer.shapeRenderer.draw(B,D,u)}}}}}if(this.highlightColors.length==0){this.highlightColors=c.jqplot.computeHighlightColors(this._dataColors)}else{if(typeof(this.highlightColors)=="string"){this.highlightColors=[];for(var E=0;E<this._dataColors.length;E++){this.highlightColors.push(this.highlightColors)}}}};function b(l,k,h){h=h||{};h.axesDefaults=h.axesDefaults||{};h.grid=h.grid||{};h.legend=h.legend||{};h.seriesDefaults=h.seriesDefaults||{};var g=false;if(h.seriesDefaults.renderer===c.jqplot.PyramidRenderer){g=true}else{if(h.series){for(var j=0;j<h.series.length;j++){if(h.series[j].renderer===c.jqplot.PyramidRenderer){g=true}}}}if(g){h.axesDefaults.renderer=c.jqplot.PyramidAxisRenderer;h.grid.renderer=c.jqplot.PyramidGridRenderer;h.seriesDefaults.pointLabels={show:false}}}function f(){if(this.plugins.pyramidRenderer&&this.plugins.pyramidRenderer.highlightCanvas){this.plugins.pyramidRenderer.highlightCanvas.resetCanvas();this.plugins.pyramidRenderer.highlightCanvas=null}this.plugins.pyramidRenderer={highlightedSeriesIndex:null};this.plugins.pyramidRenderer.highlightCanvas=new c.jqplot.GenericCanvas();this.eventCanvas._elem.before(this.plugins.pyramidRenderer.highlightCanvas.createElement(this._gridPadding,"jqplot-pyramidRenderer-highlight-canvas",this._plotDimensions,this));this.plugins.pyramidRenderer.highlightCanvas.setContext();this.eventCanvas._elem.bind("mouseleave",{plot:this},function(g){d(g.data.plot)})}function a(m,l,j,i){var h=m.series[l];var g=m.plugins.pyramidRenderer.highlightCanvas;g._ctx.clearRect(0,0,g._ctx.canvas.width,g._ctx.canvas.height);h._highlightedPoint=j;m.plugins.pyramidRenderer.highlightedSeriesIndex=l;var k={fillStyle:h.highlightColors[j],fillRect:false};h.renderer.shapeRenderer.draw(g._ctx,i,k);if(h.synchronizeHighlight!==false&&m.series.length>=h.synchronizeHighlight&&h.synchronizeHighlight!==l){h=m.series[h.synchronizeHighlight];k={fillStyle:h.highlightColors[j],fillRect:false};h.renderer.shapeRenderer.draw(g._ctx,h._barPoints[j],k)}g=null}function d(j){var g=j.plugins.pyramidRenderer.highlightCanvas;g._ctx.clearRect(0,0,g._ctx.canvas.width,g._ctx.canvas.height);for(var h=0;h<j.series.length;h++){j.series[h]._highlightedPoint=null}j.plugins.pyramidRenderer.highlightedSeriesIndex=null;j.target.trigger("jqplotDataUnhighlight");g=null}function e(k,j,n,m,l){if(m){var i=[m.seriesIndex,m.pointIndex,m.data];var h=jQuery.Event("jqplotDataMouseOver");h.pageX=k.pageX;h.pageY=k.pageY;l.target.trigger(h,i);if(l.series[i[0]].highlightMouseOver&&!(i[0]==l.plugins.pyramidRenderer.highlightedSeriesIndex&&i[1]==l.series[i[0]]._highlightedPoint)){var g=jQuery.Event("jqplotDataHighlight");g.which=k.which;g.pageX=k.pageX;g.pageY=k.pageY;l.target.trigger(g,i);a(l,m.seriesIndex,m.pointIndex,m.points)}}else{if(m==null){d(l)}}}c.jqplot.preInitHooks.push(b)})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.trendline.js b/web/extensions/jqplot/plugins/jqplot.trendline.js new file mode 100644 index 0000000..666498a --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.trendline.js @@ -0,0 +1,223 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.8 + * Revision: 1250 + * + * Copyright (c) 2009-2013 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + + /** + * Class: $.jqplot.Trendline + * Plugin which will automatically compute and draw trendlines for plotted data. + */ + $.jqplot.Trendline = function() { + // Group: Properties + + // prop: show + // Wether or not to show the trend line. + this.show = $.jqplot.config.enablePlugins; + // prop: color + // CSS color spec for the trend line. + // By default this wil be the same color as the primary line. + this.color = '#666666'; + // prop: renderer + // Renderer to use to draw the trend line. + // The data series that is plotted may not be rendered as a line. + // Therefore, we use our own line renderer here to draw a trend line. + this.renderer = new $.jqplot.LineRenderer(); + // prop: rendererOptions + // Options to pass to the line renderer. + // By default, markers are not shown on trend lines. + this.rendererOptions = {marker:{show:false}}; + // prop: label + // Label for the trend line to use in the legend. + this.label = ''; + // prop: type + // Either 'exponential', 'exp', or 'linear'. + this.type = 'linear'; + // prop: shadow + // true or false, whether or not to show the shadow. + this.shadow = true; + // prop: markerRenderer + // Renderer to use to draw markers on the line. + // I think this is wrong. + this.markerRenderer = {show:false}; + // prop: lineWidth + // Width of the trend line. + this.lineWidth = 1.5; + // prop: shadowAngle + // Angle of the shadow on the trend line. + this.shadowAngle = 45; + // prop: shadowOffset + // pixel offset for each stroke of the shadow. + this.shadowOffset = 1.0; + // prop: shadowAlpha + // Alpha transparency of the shadow. + this.shadowAlpha = 0.07; + // prop: shadowDepth + // number of strokes to make of the shadow. + this.shadowDepth = 3; + this.isTrendline = true; + + }; + + $.jqplot.postSeriesInitHooks.push(parseTrendLineOptions); + $.jqplot.postDrawSeriesHooks.push(drawTrendline); + $.jqplot.addLegendRowHooks.push(addTrendlineLegend); + + // called witin scope of the legend object + // current series passed in + // must return null or an object {label:label, color:color} + function addTrendlineLegend(series) { + var ret = null; + if (series.trendline && series.trendline.show) { + var lt = series.trendline.label.toString(); + if (lt) { + ret = {label:lt, color:series.trendline.color}; + } + } + return ret; + } + + // called within scope of a series + function parseTrendLineOptions (target, data, seriesDefaults, options, plot) { + if (this._type && (this._type === 'line' || this._type == 'bar')) { + this.trendline = new $.jqplot.Trendline(); + options = options || {}; + $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline); + this.trendline.renderer.init.call(this.trendline, null); + } + } + + // called within scope of series object + function drawTrendline(sctx, options) { + // if we have options, merge trendline options in with precedence + options = $.extend(true, {}, this.trendline, options); + + if (this.trendline && options.show) { + var fit; + // this.renderer.setGridData.call(this); + var data = options.data || this.data; + fit = fitData(data, this.trendline.type); + var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data); + this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow}); + } + } + + function regression(x, y, typ) { + var type = (typ == null) ? 'linear' : typ; + var N = x.length; + var slope; + var intercept; + var SX = 0; + var SY = 0; + var SXX = 0; + var SXY = 0; + var SYY = 0; + var Y = []; + var X = []; + + if (type == 'linear') { + X = x; + Y = y; + } + else if (type == 'exp' || type == 'exponential') { + for ( var i=0; i<y.length; i++) { + // ignore points <= 0, log undefined. + if (y[i] <= 0) { + N--; + } + else { + X.push(x[i]); + Y.push(Math.log(y[i])); + } + } + } + + for ( var i = 0; i < N; i++) { + SX = SX + X[i]; + SY = SY + Y[i]; + SXY = SXY + X[i]* Y[i]; + SXX = SXX + X[i]* X[i]; + SYY = SYY + Y[i]* Y[i]; + } + + slope = (N*SXY - SX*SY)/(N*SXX - SX*SX); + intercept = (SY - slope*SX)/N; + + return [slope, intercept]; + } + + function linearRegression(X,Y) { + var ret; + ret = regression(X,Y,'linear'); + return [ret[0],ret[1]]; + } + + function expRegression(X,Y) { + var ret; + var x = X; + var y = Y; + ret = regression(x, y,'exp'); + var base = Math.exp(ret[0]); + var coeff = Math.exp(ret[1]); + return [base, coeff]; + } + + function fitData(data, typ) { + var type = (typ == null) ? 'linear' : typ; + var ret; + var res; + var x = []; + var y = []; + var ypred = []; + + for (i=0; i<data.length; i++){ + if (data[i] != null && data[i][0] != null && data[i][1] != null) { + x.push(data[i][0]); + y.push(data[i][1]); + } + } + + if (type == 'linear') { + ret = linearRegression(x,y); + for ( var i=0; i<x.length; i++){ + res = ret[0]*x[i] + ret[1]; + ypred.push([x[i], res]); + } + } + else if (type == 'exp' || type == 'exponential') { + ret = expRegression(x,y); + for ( var i=0; i<x.length; i++){ + res = ret[1]*Math.pow(ret[0],x[i]); + ypred.push([x[i], res]); + } + } + return {data: ypred, slope: ret[0], intercept: ret[1]}; + } + +})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/plugins/jqplot.trendline.min.js b/web/extensions/jqplot/plugins/jqplot.trendline.min.js new file mode 100644 index 0000000..5474435 --- /dev/null +++ b/web/extensions/jqplot/plugins/jqplot.trendline.min.js @@ -0,0 +1,3 @@ +/* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com + jsDate | (c) 2010-2013 Chris Leonello + */(function(f){f.jqplot.Trendline=function(){this.show=f.jqplot.config.enablePlugins;this.color="#666666";this.renderer=new f.jqplot.LineRenderer();this.rendererOptions={marker:{show:false}};this.label="";this.type="linear";this.shadow=true;this.markerRenderer={show:false};this.lineWidth=1.5;this.shadowAngle=45;this.shadowOffset=1;this.shadowAlpha=0.07;this.shadowDepth=3;this.isTrendline=true};f.jqplot.postSeriesInitHooks.push(e);f.jqplot.postDrawSeriesHooks.push(g);f.jqplot.addLegendRowHooks.push(a);function a(k){var j=null;if(k.trendline&&k.trendline.show){var i=k.trendline.label.toString();if(i){j={label:i,color:k.trendline.color}}}return j}function e(m,k,j,i,l){if(this._type&&(this._type==="line"||this._type=="bar")){this.trendline=new f.jqplot.Trendline();i=i||{};f.extend(true,this.trendline,{color:this.color},j.trendline,i.trendline);this.trendline.renderer.init.call(this.trendline,null)}}function g(m,i){i=f.extend(true,{},this.trendline,i);if(this.trendline&&i.show){var k;var l=i.data||this.data;k=c(l,this.trendline.type);var j=i.gridData||this.renderer.makeGridData.call(this,k.data);this.trendline.renderer.draw.call(this.trendline,m,j,{showLine:true,shadow:this.trendline.shadow})}}function b(w,v,n){var u=(n==null)?"linear":n;var s=w.length;var t;var z;var o=0;var m=0;var r=0;var q=0;var l=0;var j=[];var k=[];if(u=="linear"){k=w;j=v}else{if(u=="exp"||u=="exponential"){for(var p=0;p<v.length;p++){if(v[p]<=0){s--}else{k.push(w[p]);j.push(Math.log(v[p]))}}}}for(var p=0;p<s;p++){o=o+k[p];m=m+j[p];q=q+k[p]*j[p];r=r+k[p]*k[p];l=l+j[p]*j[p]}t=(s*q-o*m)/(s*r-o*o);z=(m-t*o)/s;return[t,z]}function h(k,j){var i;i=b(k,j,"linear");return[i[0],i[1]]}function d(o,m){var k;var i=o;var n=m;k=b(i,n,"exp");var l=Math.exp(k[0]);var j=Math.exp(k[1]);return[l,j]}function c(l,j){var p=(j==null)?"linear":j;var n;var o;var r=[];var q=[];var m=[];for(k=0;k<l.length;k++){if(l[k]!=null&&l[k][0]!=null&&l[k][1]!=null){r.push(l[k][0]);q.push(l[k][1])}}if(p=="linear"){n=h(r,q);for(var k=0;k<r.length;k++){o=n[0]*r[k]+n[1];m.push([r[k],o])}}else{if(p=="exp"||p=="exponential"){n=d(r,q);for(var k=0;k<r.length;k++){o=n[1]*Math.pow(n[0],r[k]);m.push([r[k],o])}}}return{data:m,slope:n[0],intercept:n[1]}}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jqplot/usage.txt b/web/extensions/jqplot/usage.txt new file mode 100644 index 0000000..d2bc33d --- /dev/null +++ b/web/extensions/jqplot/usage.txt @@ -0,0 +1,126 @@ +Title: jqPlot Usage + +Usage Documentation: + +Introduction: + +jqPlot is a jQuery plugin to generate pure client-side javascript charts in your web pages. + +The jqPlot home page is at <http://www.jqplot.com/>. + +The project page and downloads are at <http://www.bitbucket.org/cleonello/jqplot/>. + +Below are a few examples to demonstrate jqPlot usage. These plots are shown as static images. +Many more examples of dynamically rendered plots can be seen on the test and examples pages here: <../../tests/>. + +Include the Files: + +jqPlot requires jQuery (1.4+ required for certain features). jQuery is included in the distribution. +To use jqPlot include jquery, the jqPlot jQuery plugin, jqPlot css file and optionally the excanvas +script for IE support in your web page. Note, excanvas is required only for IE versions below 9. IE 9 includes +native support for the canvas element and does not require excanvas: + +> <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]--> +> <script language="javascript" type="text/javascript" src="jquery.min.js"></script> +> <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script> +> <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" /> + +Add a plot container: + +Add a container (target) to your web page where you want your plot to show up. +Be sure to give your target a width and a height: + +> <div id="chartdiv" style="height:400px;width:300px; "></div> + +Create a plot: + +Then, create the actual plot by calling the +$.jqplot plugin with the id of your target and some data: + +> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]); + +Which will produce a +chart like: + +(see images/basicline.png) + +Plot Options: + +You can customize the plot by passing options to the $.jqplot function. Options are described in +<jqPlot Options> in the jqPlotOptions.txt file. An example of options usage: + +> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]], +> { title:'Exponential Line', +> axes:{yaxis:{min:-10, max:240}}, +> series:[{color:'#5FAB78'}] +> }); + +Which will produce +a plot like: + +(see images/basicoptions.png) + +Using Plugins: + +You can use jqPlot plugins (that is, plugins to the jqPlot plugin) by including them in your html +after you include the jqPlot plugin. Here is how to include the log axis plugin: + +> <link rel="stylesheet" type="text/css" href="jquery.jqplot.css" /> +> <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]--> +> <script language="javascript" type="text/javascript" src="jquery.min.js"></script> +> <script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script> +> <script language="javascript" type="text/javascript" src="jqplot.logAxisRenderer.js"></script> + +Important note: For jqplot builds r529 and above (0.9.7r529 and higher), you must explicitly +enable plugins via either the { show: true } plugin option to the plot or by using +the $.jqplot.config.enablePlugins = true; config options set on the page before plot creation. +Only plugins that can be immediately active upon loading are affected. This includes +non-renderer plugins like cursor, dragable, highlighter, and trendline. + +Here is the same $.jqplot call +but with a log y axis: + +> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]], +> { title:'Exponential Line', +> axes:{yaxis:{renderer: $.jqplot.LogAxisRenderer}}, +> series:[{color:'#5FAB78'}] +> }); + +Which produces +a plot like: + +(see images/basiclogaxis.png) + +You can further customize with options specific +to the log axis plugin: + +> $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]], +> { title:'Exponential Line', +> axes:{yaxis:{renderer: $.jqplot.LogAxisRenderer, tickDistribution:'power'}}, +> series:[{color:'#5FAB78'}] +> }); + +Which makes a +plot like: + +(see images/basiclogoptions.png) + +For a full list of options, see <jqPlot Options> in the jqPlotOptions.txt file. + +You can add as many plugins as you wish. Order is generally not important. +Some plugins, like the highlighter plugin which highlights data points near the +mouse, don't need any extra options or setup to function. Highlighter does have +additional options which the user can set. + +Other plugins, the barRenderer for example, provide functionality that must be specified +in the chart options object. To render a series as a bar graph with the bar renderer, +you would first include the plugin after jqPlot: + +> <script language="javascript" type="text/javascript" src="plugins/jqplot.barRenderer.min.js"></script> + +Then you would create +a chart like: + +> $.jqplot('chartdiv', [[34.53, 56.32, 25.1, 18.6]], {series:[{renderer:$.jqplot.BarRenderer}]}); + +Here the default LineRenderer is replaced by a BarRenderer to generate a bar graph for the first (and only) series. diff --git a/web/extensions/jquery/js/jquery-1.10.2.min.js b/web/extensions/jquery/js/jquery-1.10.2.min.js new file mode 100644 index 0000000..da41706 --- /dev/null +++ b/web/extensions/jquery/js/jquery-1.10.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav></:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Ct=/^(?:checkbox|radio)$/i,Nt=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("<div>").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window); diff --git a/web/extensions/jquery/js/jquery-1.8.3-dev.js b/web/extensions/jquery/js/jquery-1.8.3-dev.js new file mode 100644 index 0000000..14ba636 --- /dev/null +++ b/web/extensions/jquery/js/jquery-1.8.3-dev.js @@ -0,0 +1,9472 @@ +/*! + * jQuery JavaScript Library v1.8.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) + */ +(function( window, undefined ) { +var + // A central reference to the root jQuery(document) + rootjQuery, + + // The deferred used on DOM ready + readyList, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + navigator = window.navigator, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Save a reference to some core methods + core_push = Array.prototype.push, + core_slice = Array.prototype.slice, + core_indexOf = Array.prototype.indexOf, + core_toString = Object.prototype.toString, + core_hasOwn = Object.prototype.hasOwnProperty, + core_trim = String.prototype.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, + + // Used for detecting and trimming whitespace + core_rnotwhite = /\S/, + core_rspace = /\s+/, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // The ready event handler and self cleanup method + DOMContentLoaded = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + } else if ( document.readyState === "complete" ) { + // we're here because readyState === "complete" in oldIE + // which is good enough for us to call the dom ready! + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context && context.nodeType ? context.ownerDocument || context : document ); + + // scripts is true for back-compat + selector = jQuery.parseHTML( match[1], doc, true ); + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + this.attr.call( selector, context, true ); + } + + return jQuery.merge( this, selector ); + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.8.3", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ), + "slice", core_slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ core_toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // scripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, scripts ) { + var parsed; + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + scripts = context; + context = 0; + } + context = context || document; + + // Single tag + if ( (parsed = rsingleTag.exec( data )) ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); + return jQuery.merge( [], + (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); + }, + + parseJSON: function( data ) { + if ( !data || typeof data !== "string") { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && core_rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var name, + i = 0, + length = obj.length, + isObj = length === undefined || jQuery.isFunction( obj ); + + if ( args ) { + if ( isObj ) { + for ( name in obj ) { + if ( callback.apply( obj[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( obj[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in obj ) { + if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var type, + ret = results || []; + + if ( arr != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + type = jQuery.type( arr ); + + if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { + core_push.call( ret, arr ); + } else { + jQuery.merge( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, + ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready, 1 ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.split( core_rspace ), function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + return jQuery.inArray( fn, list ) > -1; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? + function() { + var returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + } : + newDefer[ action ] + ); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] = list.fire + deferred[ tuple[0] ] = list.fire; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + eventName, + i, + isSupported, + clickFn, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: ( document.compatMode === "CSS1Compat" ), + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", clickFn = function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent("onclick"); + div.detachEvent( "onclick", clickFn ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + input.setAttribute( "checked", "checked" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: true, + change: true, + focusin: true + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Run tests that need a body at doc ready + jQuery(function() { + var container, div, tds, marginDiv, + divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = document.createElement("div"); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "<div></div>"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + container.style.zoom = 1; + } + + // Null elements to avoid leaks in IE + body.removeChild( container ); + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + fragment.removeChild( div ); + all = a = select = opt = input = fragment = div = null; + + return support; +})(); +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + deletedIds: [], + + // Remove at next major release (1.9/2.0) + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, key, true ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, fixSpecified, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea|)$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var removes, className, elem, c, cl, i, l; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + if ( (value && typeof value === "string") || value === undefined ) { + removes = ( value || "" ).split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + if ( elem.nodeType === 1 && elem.className ) { + + className = (" " + elem.className + " ").replace( rclass, " " ); + + // loop over each item in the removal list + for ( c = 0, cl = removes.length; c < cl; c++ ) { + // Remove until there is nothing to remove, + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { + className = className.replace( " " + removes[ c ] + " " , " " ); + } + } + elem.className = value ? jQuery.trim( className ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( core_rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 + attrFn: {}, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + + attrNames = value.split( core_rspace ); + + for ( ; i < attrNames.length; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.value = value + "" ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var t, tns, type, origType, namespaces, origCount, + j, events, special, eventType, handleObj, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, "events", true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, + type = event.type || event, + namespaces = []; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + for ( old = elem; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old === (elem.ownerDocument || document) ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, + handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = core_slice.call( arguments ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = []; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) + event.metaKey = !!event.metaKey; + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "_submit_attached" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "_submit_attached", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "_change_attached", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = "<a href='#'></a>"; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = "<select></select>"; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = "<select><option selected=''></option></select>"; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "<p test=''></p>"; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = "<input type='hidden'/>"; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, l, length, n, r, ret, + self = this; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + ret = this.pushStack( "", "find", selector ); + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /<tbody/i, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + rnocache = /<(?:script|object|embed|option|style)/i, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rcheckableType = /^(?:checkbox|radio)$/, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g, + wrapMap = { + option: [ 1, "<select multiple='multiple'>", "</select>" ], + legend: [ 1, "<fieldset>", "</fieldset>" ], + thead: [ 1, "<table>", "</table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], + area: [ 1, "<map>", "</map>" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, +// unless wrapped in a div with non-breaking characters in front of it. +if ( !jQuery.support.htmlSerialize ) { + wrapMap._default = [ 1, "X<div>", "</div>" ]; +} + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); + } + }, + + after: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); + } + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1></$2>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + if ( !isDisconnected( this[0] ) ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } + + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = [].concat.apply( [], args ); + + var results, first, fragment, iNoClone, + i = 0, + value = args[0], + scripts = [], + l = this.length; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call( this, i, table ? self.html() : undefined ); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + results = jQuery.buildFragment( args, this, scripts ); + fragment = results.fragment; + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + // Fragments from the fragment cache must always be cloned and never used in place. + for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + i === iNoClone ? + fragment : + jQuery.clone( fragment, true, true ) + ); + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + + if ( scripts.length ) { + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + if ( jQuery.ajax ) { + jQuery.ajax({ + url: elem.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.error("no ajax"); + } + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function cloneFixAttributes( src, dest ) { + var nodeName; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + // clearAttributes removes the attributes, which we don't want, + // but also removes the attachEvent events, which we *do* want + if ( dest.clearAttributes ) { + dest.clearAttributes(); + } + + // mergeAttributes, in contrast, only merges back on the + // original attributes, not the events + if ( dest.mergeAttributes ) { + dest.mergeAttributes( src ); + } + + nodeName = dest.nodeName.toLowerCase(); + + if ( nodeName === "object" ) { + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; + } + + // Event data gets referenced instead of copied if the expando + // gets copied too + dest.removeAttribute( jQuery.expando ); +} + +jQuery.buildFragment = function( args, context, scripts ) { + var fragment, cacheable, cachehit, + first = args[ 0 ]; + + // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { + + // Mark cacheable and look for a hit + cacheable = true; + fragment = jQuery.fragments[ first ]; + cachehit = fragment !== undefined; + } + + if ( !fragment ) { + fragment = context.createDocumentFragment(); + jQuery.clean( args, context, fragment, scripts ); + + // Update the cache, but only store false + // unless this is a second parsing of the same content + if ( cacheable ) { + jQuery.fragments[ first ] = cachehit && fragment; + } + } + + return { fragment: fragment, cacheable: cacheable }; +}; + +jQuery.fragments = {}; + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + l = insert.length, + parent = this.length === 1 && this[0].parentNode; + + if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { + insert[ original ]( this[0] ); + return this; + } else { + for ( ; i < l; i++ ) { + elems = ( i > 0 ? this.clone(true) : this ).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +}); + +function getAll( elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { + return elem.getElementsByTagName( "*" ); + + } else if ( typeof elem.querySelectorAll !== "undefined" ) { + return elem.querySelectorAll( "*" ); + + } else { + return []; + } +} + +// Used in clean, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var srcElements, + destElements, + i, + clone; + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }, + + clean: function( elems, context, fragment, scripts ) { + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, + ret = []; + + // Ensure that context is a document + if ( !context || typeof context.createDocumentFragment === "undefined" ) { + context = document; + } + + // Use the already-created safe fragment if context permits + for ( i = 0; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Ensure a safe container in which to render the html + safe = safe || createSafeFragment( context ); + div = context.createElement("div"); + safe.appendChild( div ); + + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1></$2>"); + + // Go to html and back, then peel off extra wrappers + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + depth = wrap[0]; + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted <tbody> from table fragments + if ( !jQuery.support.tbody ) { + + // String was a <table>, *may* have spurious <tbody> + hasBody = rtbody.test(elem); + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare <thead> or <tfoot> + wrap[1] === "<table>" && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + jQuery.merge( ret, elem ); + } + } + + // Fix #11356: Clear elements from safeFragment + if ( div ) { + elem = div = safe = null; + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + for ( i = 0; (elem = ret[i]) != null; i++ ) { + if ( jQuery.nodeName( elem, "input" ) ) { + fixDefaultChecked( elem ); + } else if ( typeof elem.getElementsByTagName !== "undefined" ) { + jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); + } + } + } + + // Append elements to a provided document fragment + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var data, id, elem, type, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + jQuery.deletedIds.push( id ); + } + } + } + } + } +}); +// Limit scope pollution from any deprecated API +(function() { + +var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; +}; + +})(); +var curCSS, iframe, iframeDoc, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity=([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], + + eventsToggle = jQuery.fn.toggle; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var elem, display, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + values[ index ] = jQuery._data( elem, "olddisplay" ); + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && elem.style.display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + display = curCSS( elem, "display" ); + + if ( !values[ index ] && display !== "none" ) { + jQuery._data( elem, "olddisplay", display ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state, fn2 ) { + var bool = typeof state === "boolean"; + + if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { + return eventsToggle.apply( this, arguments ); + } + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, numeric, extra ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( numeric || extra !== undefined ) { + num = parseFloat( val ); + return numeric || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + curCSS = function( elem, name ) { + var ret, width, minWidth, maxWidth, + computed = window.getComputedStyle( elem, null ), + style = elem.style; + + if ( computed ) { + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + curCSS = function( elem, name ) { + var left, rsLeft, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + // we use jQuery.css instead of curCSS here + // because of the reliableMarginRight CSS hook! + val += jQuery.css( elem, extra + cssExpand[ i ], true ); + } + + // From this point on we use curCSS for maximum performance (relevant in animations) + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } else { + // at this point, extra isn't content, so add padding + val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + valueIsBorderBox = true, + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox + ) + ) + "px"; +} + + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + if ( elemdisplay[ nodeName ] ) { + return elemdisplay[ nodeName ]; + } + + var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), + display = elem.css("display"); + elem.remove(); + + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // Use the already-created iframe if possible + iframe = document.body.appendChild( + iframe || jQuery.extend( document.createElement("iframe"), { + frameBorder: 0, + width: 0, + height: 0 + }) + ); + + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write("<!doctype html><html><body>"); + iframeDoc.close(); + } + + elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); + + display = curCSS( elem, "display" ); + document.body.removeChild( iframe ); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + + return display; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }); + } else { + return getWidthOrHeight( elem, name, extra ); + } + } + }, + + set: function( elem, value, extra ) { + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" + ) : 0 + ); + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && + style.removeAttribute ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +// These hooks cannot be added until DOM ready because the support test +// for it is not run until after DOM ready +jQuery(function() { + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "marginRight" ); + } + }); + } + }; + } + + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // getComputedStyle returns percent when specified for top/left/bottom/right + // rather than make the css module depend on the offset module, we just check for it here + if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { + jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = { + get: function( elem, computed ) { + if ( computed ) { + var ret = curCSS( elem, prop ); + // if curCSS returns percentage, fallback to offset + return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; + } + } + }; + }); + } + +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rselectTextarea = /^(?:select|textarea)/i; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +//Serialize an array of form elements or a set of +//key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} +var + // Document location + ajaxLocParts, + ajaxLocation, + + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, list, placeBefore, + dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), + i = 0, + length = dataTypes.length; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var selection, + list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ); + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); + } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } +} + +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + // Don't do a request if no elements are being requested + if ( !this.length ) { + return this; + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // Request the remote document + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params, + complete: function( jqXHR, status ) { + if ( callback ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + } + } + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + // See if a selector was specified + self.html( selector ? + + // Create a dummy div to hold the results + jQuery("<div>") + + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append( responseText.replace( rscript, "" ) ) + + // Locate the specified elements + .find( selector ) : + + // If not, just inject the full result + responseText ); + + }); + + return this; +}; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +}); + +jQuery.extend({ + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); + } else { + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; + } + ajaxExtend( target, settings ); + return target; + }, + + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true + } + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // ifModified key + ifModifiedKey, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || strAbort; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ ifModifiedKey ] = modified; + } + modified = jqXHR.getResponseHeader("Etag"); + if ( modified ) { + jQuery.etag[ ifModifiedKey ] = modified; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + isSuccess = ajaxConvert( s, response ); + statusText = isSuccess.state; + success = isSuccess.data; + error = isSuccess.error; + isSuccess = !error; + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.always( tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + + } + + // aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + return jqXHR; + }, + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + + var conv, conv2, current, tmp, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(), + prev = dataTypes[ 0 ], + converters = {}, + i = 0; + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + // Convert to each sequential dataType, tolerating list modification + for ( ; (current = dataTypes[++i]); ) { + + // There's only work to do if current dataType is non-auto + if ( current !== "*" ) { + + // Convert response if prev dataType is non-auto and differs from current + if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split(" "); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.splice( i--, 0, current ); + } + + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s["throws"] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + + // Update prev for next iteration + prev = current; + } + } + + return { state: "success", data: response }; +} +var oldCallbacks = [], + rquestion = /\?/, + rjsonp = /(=)\?(?=&|$)|\?\?/, + nonce = jQuery.now(); + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + data = s.data, + url = s.url, + hasCallback = s.jsonp !== false, + replaceInUrl = hasCallback && rjsonp.test( url ), + replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && + !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && + rjsonp.test( data ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + overwritten = window[ callbackName ]; + + // Insert callback into url or form data + if ( replaceInUrl ) { + s.url = url.replace( rjsonp, "$1" + callbackName ); + } else if ( replaceInData ) { + s.data = data.replace( rjsonp, "$1" + callbackName ); + } else if ( hasCallback ) { + s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } +}); +var xhrCallbacks, + // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +(function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); +})( jQuery.ajaxSettings.xhr() ); + +// Create transport if the browser can provide an xhr +if ( jQuery.support.ajax ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var handle, i, + xhr = s.xhr(); + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + + var status, + statusText, + responseHeaders, + responses, + xml; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occurred + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + responses = {}; + xml = xhr.responseXML; + + // Construct response list + if ( xml && xml.documentElement /* #4958 */ ) { + responses.xml = xml; + } + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( e ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + if ( !s.async ) { + // if we're in sync mode we fire the callback + callback(); + } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been + // retrieved directly we need to fire the callback + setTimeout( callback, 0 ); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback(0,1); + } + } + }; + } + }); +} +var fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [function( prop, value ) { + var end, unit, + tween = this.createTween( prop, value ), + parts = rfxnum.exec( value ), + target = tween.cur(), + start = +target || 0, + scale = 1, + maxIterations = 20; + + if ( parts ) { + end = +parts[2]; + unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" && start ) { + // Iteratively approximate from a nonzero starting point + // Prefer the current property, because this process will be trivial if it uses the same units + // Fallback to end or a simple constant + start = jQuery.css( tween.elem, prop, true ) || end || 1; + + do { + // If previous iteration zeroed out, double until we get *something* + // Use a string for doubling factor so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + tween.unit = unit; + tween.start = start; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; + } + return tween; + }] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }, 0 ); + return ( fxNow = jQuery.now() ); +} + +function createTweens( animation, props ) { + jQuery.each( props, function( prop, value ) { + var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( collection[ index ].call( animation, prop, value ) ) { + + // we're done with this property + return; + } + } + }); +} + +function Animation( elem, properties, options ) { + var result, + index = 0, + tweenerIndex = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end, easing ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // if we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // resolve when we played the last frame + // otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + createTweens( animation, props ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + anim: animation, + queue: animation.opts.queue, + elem: elem + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'index' from above because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +function defaultPrefilter( elem, props, opts ) { + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, + anim = this, + style = elem.style, + orig = {}, + handled = [], + hidden = elem.nodeType && isHidden( elem ); + + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // doing this makes sure that the complete handler will be called + // before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( elem, "display" ) === "inline" && + jQuery.css( elem, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { + style.display = "inline-block"; + + } else { + style.zoom = 1; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + if ( !jQuery.support.shrinkWrapBlocks ) { + anim.done(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + } + + + // show/hide pass + for ( index in props ) { + value = props[ index ]; + if ( rfxtypes.exec( value ) ) { + delete props[ index ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + continue; + } + handled.push( index ); + } + } + + length = handled.length; + if ( length ) { + dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + jQuery.removeData( elem, "fxshow", true ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( index = 0 ; index < length ; index++ ) { + prop = handled[ index ]; + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + } +} + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // passing any value as a 4th parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails + // so, simple values such as "10px" are parsed to Float. + // complex values such as "rotate(1rad)" are returned as is. + result = jQuery.css( tween.elem, tween.prop, false, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // use step hook for back compat - use cssHook if its there - use .style if its + // available and use plain properties where available + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Remove in 2.0 - this supports IE8's panic based approach +// to setting things on disconnected nodes + +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" || + // special check for .toggle( handler, handler, ... ) + ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations resolve immediately + if ( empty ) { + anim.stop( true ); + } + }; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = jQuery._data( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + } +}); + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + attrs = { height: type }, + i = 0; + + // if we include width, step value is 1 to do all cssExpand values, + // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; + for( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p*Math.PI ) / 2; + } +}; + +jQuery.timers = []; +jQuery.fx = Tween.prototype.init; +jQuery.fx.tick = function() { + var timer, + timers = jQuery.timers, + i = 0; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + if ( timer() && jQuery.timers.push( timer ) && !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} +var rroot = /^(?:body|html)$/i; + +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, + elem = this[ 0 ], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + if ( (body = doc.body) === elem ) { + return jQuery.offset.bodyOffset( elem ); + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + clientTop = docElem.clientTop || body.clientTop || 0; + clientLeft = docElem.clientLeft || body.clientLeft || 0; + scrollTop = win.pageYOffset || docElem.scrollTop; + scrollLeft = win.pageXOffset || docElem.scrollLeft; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; +}; + +jQuery.offset = { + + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; + + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; + } + + return { top: top, left: left }; + }, + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || document.body; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + win.document.documentElement[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return jQuery.access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest + // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, value, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +})( window ); diff --git a/web/extensions/jquery/js/jquery-ui-1.10.3.custom.min.js b/web/extensions/jquery/js/jquery-ui-1.10.3.custom.min.js new file mode 100644 index 0000000..e23540d --- /dev/null +++ b/web/extensions/jquery/js/jquery-ui-1.10.3.custom.min.js @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.10.3 - 2013-08-16 +* http://jqueryui.com +* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js +* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */ + +(function(e,t){function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return"area"===o?(a=t.parentNode,n=a.name,t.href&&n&&"map"===a.nodeName.toLowerCase()?(r=e("img[usemap=#"+n+"]")[0],!!r&&s(r)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var a=0,n=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.3",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var s,a,n=e(this[0]);n.length&&n[0]!==document;){if(s=n.css("position"),("absolute"===s||"relative"===s||"fixed"===s)&&(a=parseInt(n.css("zIndex"),10),!isNaN(a)&&0!==a))return a;n=n.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})},removeUniqueId:function(){return this.each(function(){n.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var s=e.attr(t,"tabindex"),a=isNaN(s);return(a||s>=0)&&i(t,!a)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,s){function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===s?["Left","Right"]:["Top","Bottom"],r=s.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+s]=function(i){return i===t?o["inner"+s].call(this):this.each(function(){e(this).css(r,a(this,i)+"px")})},e.fn["outer"+s]=function(t,i){return"number"!=typeof t?o["outer"+s].call(this,t):this.each(function(){e(this).css(r,a(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,s){var a,n=e.ui[t].prototype;for(a in s)n.plugins[a]=n.plugins[a]||[],n.plugins[a].push([i,s[a]])},call:function(e,t,i){var s,a=e.plugins[t];if(a&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(s=0;a.length>s;s++)e.options[a[s][0]]&&a[s][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",a=!1;return t[s]>0?!0:(t[s]=1,a=t[s]>0,t[s]=0,a)}})})(jQuery);(function(e,t){var i=0,s=Array.prototype.slice,n=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler("remove")}catch(a){}n(t)},e.widget=function(i,s,n){var a,r,o,h,l={},u=i.split(".")[0];i=i.split(".")[1],a=u+"-"+i,n||(n=s,s=e.Widget),e.expr[":"][a.toLowerCase()]=function(t){return!!e.data(t,a)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(n,function(i,n){return e.isFunction(n)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,a=this._superApply;return this._super=e,this._superApply=t,i=n.apply(this,arguments),this._super=s,this._superApply=a,i}}(),t):(l[i]=n,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:a}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var n,a,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(n in r[o])a=r[o][n],r[o].hasOwnProperty(n)&&a!==t&&(i[n]=e.isPlainObject(a)?e.isPlainObject(i[n])?e.widget.extend({},i[n],a):e.widget.extend({},a):a);return i},e.widget.bridge=function(i,n){var a=n.prototype.widgetFullName||i;e.fn[i]=function(r){var o="string"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,n=e.data(this,a);return n?e.isFunction(n[r])&&"_"!==r.charAt(0)?(s=n[r].apply(n,h),s!==n&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error("no such method '"+r+"' for "+i+" widget instance"):e.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,a);t?t.option(r||{})._init():e.data(this,a,new n(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var n,a,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof i)if(o={},n=i.split("."),i=n.shift(),n.length){for(a=o[i]=e.widget.extend({},this.options[i]),r=0;n.length-1>r;r++)a[n[r]]=a[n[r]]||{},a=a[n[r]];if(i=n.pop(),s===t)return a[i]===t?null:a[i];a[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var a,r=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=a=e(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,a=this.widget()),e.each(n,function(n,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?r[o]:o).apply(r,arguments):t}"string"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=n.match(/^(\w+)\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?a.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var r,o=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),r=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),r&&e.effects&&e.effects.effect[o]?s[t](n):o!==t&&s[o]?s[o](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}})})(jQuery);(function(e){var t=!1;e(document).mouseup(function(){t=!1}),e.widget("ui.mouse",{version:"1.10.3",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!t){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?e(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===e.data(i.target,this.widgetName+".preventClickEvent")&&e.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return s._mouseMove(e)},this._mouseUpDelegate=function(e){return s._mouseUp(e)},e(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),t=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t,e){function i(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function s(e,i){return parseInt(t.css(e,i),10)||0}function n(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(a!==e)return a;var i,s,n=t("<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),o=n.children()[0];return t("body").append(n),i=o.offsetWidth,n.css("overflow","scroll"),s=o.offsetWidth,i===s&&(s=n[0].clientWidth),n.remove(),a=i-s},getScrollInfo:function(e){var i=e.isWindow?"":e.element.css("overflow-x"),s=e.isWindow?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth,a="scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight;return{width:a?t.position.scrollbarWidth():0,height:n?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]);return{element:i,isWindow:s,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s?i.width():i.outerWidth(),height:s?i.height():i.outerHeight()}}},t.fn.position=function(e){if(!e||!e.of)return f.apply(this,arguments);e=t.extend({},e);var a,p,m,g,v,b,_=t(e.of),y=t.position.getWithinInfo(e.within),w=t.position.getScrollInfo(y),x=(e.collision||"flip").split(" "),k={};return b=n(_),_[0].preventDefault&&(e.at="left top"),p=b.width,m=b.height,g=b.offset,v=t.extend({},g),t.each(["my","at"],function(){var t,i,s=(e[this]||"").split(" ");1===s.length&&(s=l.test(s[0])?s.concat(["center"]):c.test(s[0])?["center"].concat(s):["center","center"]),s[0]=l.test(s[0])?s[0]:"center",s[1]=c.test(s[1])?s[1]:"center",t=u.exec(s[0]),i=u.exec(s[1]),k[this]=[t?t[0]:0,i?i[0]:0],e[this]=[d.exec(s[0])[0],d.exec(s[1])[0]]}),1===x.length&&(x[1]=x[0]),"right"===e.at[0]?v.left+=p:"center"===e.at[0]&&(v.left+=p/2),"bottom"===e.at[1]?v.top+=m:"center"===e.at[1]&&(v.top+=m/2),a=i(k.at,p,m),v.left+=a[0],v.top+=a[1],this.each(function(){var n,l,c=t(this),u=c.outerWidth(),d=c.outerHeight(),f=s(this,"marginLeft"),b=s(this,"marginTop"),D=u+f+s(this,"marginRight")+w.width,T=d+b+s(this,"marginBottom")+w.height,C=t.extend({},v),M=i(k.my,c.outerWidth(),c.outerHeight());"right"===e.my[0]?C.left-=u:"center"===e.my[0]&&(C.left-=u/2),"bottom"===e.my[1]?C.top-=d:"center"===e.my[1]&&(C.top-=d/2),C.left+=M[0],C.top+=M[1],t.support.offsetFractions||(C.left=h(C.left),C.top=h(C.top)),n={marginLeft:f,marginTop:b},t.each(["left","top"],function(i,s){t.ui.position[x[i]]&&t.ui.position[x[i]][s](C,{targetWidth:p,targetHeight:m,elemWidth:u,elemHeight:d,collisionPosition:n,collisionWidth:D,collisionHeight:T,offset:[a[0]+M[0],a[1]+M[1]],my:e.my,at:e.at,within:y,elem:c})}),e.using&&(l=function(t){var i=g.left-C.left,s=i+p-u,n=g.top-C.top,a=n+m-d,h={target:{element:_,left:g.left,top:g.top,width:p,height:m},element:{element:c,left:C.left,top:C.top,width:u,height:d},horizontal:0>s?"left":i>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};u>p&&p>r(i+s)&&(h.horizontal="center"),d>m&&m>r(n+a)&&(h.vertical="middle"),h.important=o(r(i),r(s))>o(r(n),r(a))?"horizontal":"vertical",e.using.call(this,t,h)}),c.offset(t.extend(C,{using:l}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-o-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-o-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,m=-2*e.offset[1];0>c?(s=t.top+p+f+m+e.collisionHeight-o-a,t.top+p+f+m>c&&(0>s||r(c)>s)&&(t.top+=p+f+m)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+m-h,t.top+p+f+m>u&&(i>0||u>r(i))&&(t.top+=p+f+m))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,o=document.getElementsByTagName("body")[0],r=document.createElement("div");e=document.createElement(o?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(r),i=o||document.documentElement,i.insertBefore(e,i.firstChild),r.style.cssText="position: absolute; left: 10.7432222px;",n=t(r).offset().left,t.support.offsetFractions=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()})(jQuery);(function(e){e.widget("ui.draggable",e.ui.mouse,{version:"1.10.3",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(t){var i=this.options;return this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){e("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||e.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1):!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.element.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,e(document).width()-this.helperProportions.width-this.margins.left,(e(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(t){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=t.pageX,l=t.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s]),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i){var s=e(this).data("ui-draggable"),n=s.options,a=e.extend({},i,{item:s.element});s.sortables=[],e(n.connectToSortable).each(function(){var i=e.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",t,a))})},stop:function(t,i){var s=e(this).data("ui-draggable"),n=e.extend({},i,{item:s.element});e.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,n))})},drag:function(t,i){var s=e(this).data("ui-draggable"),n=this;e.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,e.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",t),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",t),s.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(){var t=e("body"),i=e(this).data("ui-draggable").options;t.css("cursor")&&(i._cursor=t.css("cursor")),t.css("cursor",i.cursor)},stop:function(){var t=e(this).data("ui-draggable").options;t._cursor&&e("body").css("cursor",t._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._opacity&&e(i.helper).css("opacity",s._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(){var t=e(this).data("ui-draggable");t.scrollParent[0]!==document&&"HTML"!==t.scrollParent[0].tagName&&(t.overflowOffset=t.scrollParent.offset())},drag:function(t){var i=e(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-t.pageY<s.scrollSensitivity?i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop+s.scrollSpeed:t.pageY-i.overflowOffset.top<s.scrollSensitivity&&(i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-t.pageX<s.scrollSensitivity?i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft+s.scrollSpeed:t.pageX-i.overflowOffset.left<s.scrollSensitivity&&(i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(t.pageY-e(document).scrollTop()<s.scrollSensitivity?n=e(document).scrollTop(e(document).scrollTop()-s.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<s.scrollSensitivity&&(n=e(document).scrollTop(e(document).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(t.pageX-e(document).scrollLeft()<s.scrollSensitivity?n=e(document).scrollLeft(e(document).scrollLeft()-s.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<s.scrollSensitivity&&(n=e(document).scrollLeft(e(document).scrollLeft()+s.scrollSpeed)))),n!==!1&&e.ui.ddmanager&&!s.dropBehaviour&&e.ui.ddmanager.prepareOffsets(i,t)}}),e.ui.plugin.add("draggable","snap",{start:function(){var t=e(this).data("ui-draggable"),i=t.options;t.snapElements=[],e(i.snap.constructor!==String?i.snap.items||":data(ui-draggable)":i.snap).each(function(){var i=e(this),s=i.offset();this!==t.element[0]&&t.snapElements.push({item:this,width:i.outerWidth(),height:i.outerHeight(),top:s.top,left:s.left})})},drag:function(t,i){var s,n,a,o,r,h,l,u,c,d,p=e(this).data("ui-draggable"),f=p.options,m=f.snapTolerance,g=i.offset.left,v=g+p.helperProportions.width,b=i.offset.top,y=b+p.helperProportions.height;for(c=p.snapElements.length-1;c>=0;c--)r=p.snapElements[c].left,h=r+p.snapElements[c].width,l=p.snapElements[c].top,u=l+p.snapElements[c].height,r-m>v||g>h+m||l-m>y||b>u+m||!e.contains(p.snapElements[c].item.ownerDocument,p.snapElements[c].item)?(p.snapElements[c].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(s=m>=Math.abs(l-y),n=m>=Math.abs(u-b),a=m>=Math.abs(r-v),o=m>=Math.abs(h-g),s&&(i.position.top=p._convertPositionTo("relative",{top:l-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h}).left-p.margins.left)),d=s||n||a||o,"outer"!==f.snapMode&&(s=m>=Math.abs(l-b),n=m>=Math.abs(u-y),a=m>=Math.abs(r-g),o=m>=Math.abs(h-v),s&&(i.position.top=p._convertPositionTo("relative",{top:l,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[c].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=s||n||a||o||d)}}),e.ui.plugin.add("draggable","stack",{start:function(){var t,i=this.data("ui-draggable").options,s=e.makeArray(e(i.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});s.length&&(t=parseInt(e(s[0]).css("zIndex"),10)||0,e(s).each(function(i){e(this).css("zIndex",t+i)}),this.css("zIndex",t+s.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._zIndex&&e(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(e){function t(e,t,i){return e>t&&t+i>e}e.widget("ui.droppable",{version:"1.10.3",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t=this.options,i=t.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(i)?i:function(e){return e.is(i)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},e.ui.ddmanager.droppables[t.scope]=e.ui.ddmanager.droppables[t.scope]||[],e.ui.ddmanager.droppables[t.scope].push(this),t.addClasses&&this.element.addClass("ui-droppable")},_destroy:function(){for(var t=0,i=e.ui.ddmanager.droppables[this.options.scope];i.length>t;t++)i[t]===this&&i.splice(t,1);this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){"accept"===t&&(this.accept=e.isFunction(i)?i:function(e){return e.is(i)}),e.Widget.prototype._setOption.apply(this,arguments)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var t=e.data(this,"ui-droppable");return t.options.greedy&&!t.options.disabled&&t.options.scope===s.options.scope&&t.accept.call(t.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(t,{offset:t.element.offset()}),t.options.tolerance)?(n=!0,!1):undefined}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(e,i,s){if(!i.offset)return!1;var n,a,o=(e.positionAbs||e.position.absolute).left,r=o+e.helperProportions.width,h=(e.positionAbs||e.position.absolute).top,l=h+e.helperProportions.height,u=i.offset.left,c=u+i.proportions.width,d=i.offset.top,p=d+i.proportions.height;switch(s){case"fit":return o>=u&&c>=r&&h>=d&&p>=l;case"intersect":return o+e.helperProportions.width/2>u&&c>r-e.helperProportions.width/2&&h+e.helperProportions.height/2>d&&p>l-e.helperProportions.height/2;case"pointer":return n=(e.positionAbs||e.position.absolute).left+(e.clickOffset||e.offset.click).left,a=(e.positionAbs||e.position.absolute).top+(e.clickOffset||e.offset.click).top,t(a,d,i.proportions.height)&&t(n,u,i.proportions.width);case"touch":return(h>=d&&p>=h||l>=d&&p>=l||d>h&&l>p)&&(o>=u&&c>=o||r>=u&&c>=r||u>o&&r>c);default:return!1}},e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions.height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions={width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight})}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e.data(this,"ui-droppable").options.scope===n}),a.length&&(s=e.data(a[0],"ui-droppable"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}}})(jQuery);(function(e){function t(e){return parseInt(e,10)||0}function i(e){return!isNaN(parseInt(e,10))}e.widget("ui.resizable",e.ui.mouse,{version:"1.10.3",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=e(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=t(this.helper.css("left")),n=t(this.helper.css("top")),o.containment&&(s+=e(o.containment).scrollLeft()||0,n+=e(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(t){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,u=this.size.height,c=t.pageX-a.left||0,d=t.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[t,c,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==u&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(n)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&e.ui.hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||e)&&(t=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,t>o.minWidth&&(o.minWidth=t),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(e){this.offset=this.helper.offset(),i(e.left)&&(this.position.left=e.left),i(e.top)&&(this.position.top=e.top),i(e.height)&&(this.size.height=e.height),i(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,s=this.size,n=this.axis;return i(e.height)?e.width=e.height*this.aspectRatio:i(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===n&&(e.left=t.left+(s.width-e.width),e.top=null),"nw"===n&&(e.top=t.top+(s.height-e.height),e.left=t.left+(s.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,s=this.axis,n=i(e.width)&&t.maxWidth&&t.maxWidth<e.width,a=i(e.height)&&t.maxHeight&&t.maxHeight<e.height,o=i(e.width)&&t.minWidth&&t.minWidth>e.width,r=i(e.height)&&t.minHeight&&t.minHeight>e.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,u=/sw|nw|w/.test(s),c=/nw|ne|n/.test(s);return o&&(e.width=t.minWidth),r&&(e.height=t.minHeight),n&&(e.width=t.maxWidth),a&&(e.height=t.maxHeight),o&&u&&(e.left=h-t.minWidth),n&&u&&(e.left=h-t.maxWidth),r&&c&&(e.top=l-t.minHeight),a&&c&&(e.top=l-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,i,s,n,a=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(n=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],t=0;i.length>t;t++)this.borderDif[t]=(parseInt(i[t],10)||0)+(parseInt(s[t],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&e.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=e(this).data("ui-resizable"),u=l.options,c=l.element,d=u.containment,p=d instanceof e?d.get(0):/parent/.test(d)?c.parent().get(0):d;p&&(l.containerElement=e(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(i=e(p),s=[],e(["Top","Right","Left","Bottom"]).each(function(e,n){s[e]=t(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=e.ui.hasScroll(p,"left")?p.scrollWidth:o,h=e.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(t){var i,s,n,a,o=e(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,c={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(c=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-c.left),u&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-c.left:o.offset.left-c.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-c.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=o.parentData.left),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).data("ui-resizable");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).data("ui-resizable");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size,n=t.originalSize,a=t.originalPosition,o=t.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,u=Math.round((s.width-n.width)/h)*h,c=Math.round((s.height-n.height)/l)*l,d=n.width+u,p=n.height+c,f=i.maxWidth&&d>i.maxWidth,m=i.maxHeight&&p>i.maxHeight,g=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,g&&(d+=h),v&&(p+=l),f&&(d-=h),m&&(p-=l),/^(se|s|e)$/.test(o)?(t.size.width=d,t.size.height=p):/^(ne)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.top=a.top-c):/^(sw)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.left=a.left-u):(t.size.width=d,t.size.height=p,t.position.top=a.top-c,t.position.left=a.left-u)}})})(jQuery);(function(e){e.widget("ui.selectable",e.ui.mouse,{version:"1.10.3",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(i.options.filter,i.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),i=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:i.left,top:i.top,right:i.left+t.outerWidth(),bottom:i.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var i=this,s=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(s.filter,this.element[0]),this._trigger("start",t),e(s.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=e.data(this,"selectable-item");s.startselected=!0,t.metaKey||t.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",t,{unselecting:s.element}))}),e(t.target).parents().addBack().each(function(){var s,n=e.data(this,"selectable-item");return n?(s=!t.metaKey&&!t.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",t,{selecting:n.element}):i._trigger("unselecting",t,{unselecting:n.element}),!1):undefined}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=t.pageX,h=t.pageY;return a>r&&(i=r,r=a,a=i),o>h&&(i=h,h=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:h-o}),this.selectees.each(function(){var i=e.data(this,"selectable-item"),l=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?l=!(i.left>r||a>i.right||i.top>h||o>i.bottom):"fit"===n.tolerance&&(l=i.left>a&&r>i.right&&i.top>o&&h>i.bottom),l?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",t,{selecting:i.element}))):(i.selecting&&((t.metaKey||t.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",t,{unselecting:i.element}))),i.selected&&(t.metaKey||t.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",t,{unselecting:i.element})))))}),!1}},_mouseStop:function(t){var i=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",t,{unselected:s.element})}),e(".ui-selecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",t,{selected:s.element})}),this._trigger("stop",t),this.helper.remove(),!1}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}function i(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))}t.widget("ui.sortable",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var t=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===t.axis||i(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_setOption:function(e,i){"disabled"===e?(this.options[e]=i,this.widget().toggleClass("ui-sortable-disabled",!!i)):t.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(e,i){var s=null,n=!1,a=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,a.widgetName+"-item")===a?(s=t(this),!1):undefined}),t.data(e.target,a.widgetName+"-item")===a&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,a,o=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(a=this.document.find("body"),this.storedCursor=a.css("cursor"),a.css("cursor",o.cursor),this.storedStylesheet=t("<style>*{ cursor: "+o.cursor+" !important; }</style>").appendTo(a)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!o.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,a,o=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<o.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+o.scrollSpeed:e.pageY-this.overflowOffset.top<o.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-o.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<o.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+o.scrollSpeed:e.pageX-this.overflowOffset.left<o.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-o.scrollSpeed)):(e.pageY-t(document).scrollTop()<o.scrollSensitivity?r=t(document).scrollTop(t(document).scrollTop()-o.scrollSpeed):t(window).height()-(e.pageY-t(document).scrollTop())<o.scrollSensitivity&&(r=t(document).scrollTop(t(document).scrollTop()+o.scrollSpeed)),e.pageX-t(document).scrollLeft()<o.scrollSensitivity?r=t(document).scrollLeft(t(document).scrollLeft()-o.scrollSpeed):t(window).width()-(e.pageX-t(document).scrollLeft())<o.scrollSensitivity&&(r=t(document).scrollLeft(t(document).scrollLeft()+o.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!o.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],a=this._intersectsWithPointer(s),a&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===a?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===a?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),a=this.options.axis,o={};a&&"x"!==a||(o.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),a&&"y"!==a||(o.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,a=t.left,o=a+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>a&&o>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>a&&o>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var i="x"===this.options.axis||e(this.positionAbs.top+this.offset.click.top,t.top,t.height),s="y"===this.options.axis||e(this.positionAbs.left+this.offset.click.left,t.left,t.width),n=i&&s,a=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return n?this.floating?o&&"right"===o||"down"===a?2:1:a&&("down"===a?2:1):!1},_intersectsWithSides:function(t){var i=e(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),s=e(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),n=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return this.floating&&a?"right"===a&&s||"left"===a&&!s:n&&("down"===n&&i||"up"===n&&!i)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){var i,s,n,a,o=[],r=[],h=this._connectWith();if(h&&e)for(i=h.length-1;i>=0;i--)for(n=t(h[i]),s=n.length-1;s>=0;s--)a=t.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&r.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(r.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),i=r.length-1;i>=0;i--)r[i][0].each(function(){o.push(this)});return t(o)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,a,o,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i]),s=n.length-1;s>=0;s--)a=t.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&(u.push([t.isFunction(a.options.items)?a.options.items.call(a.element[0],e,{item:this.currentItem}):t(a.options.items,a.element),a]),this.containers.push(a));for(i=u.length-1;i>=0;i--)for(o=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",o),c.push({item:h,instance:o,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,a;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),a=n.offset(),s.left=a.left,s.top=a.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)a=this.containers[i].element.offset(),this.containers[i].containerCache.left=a.left,this.containers[i].containerCache.top=a.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]).addClass(i||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?e.currentItem.children().each(function(){t("<td> </td>",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,a,o,r,h,l,c,u,d,p,f=null,m=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],m=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[m].containerCache.over||(this.containers[m]._trigger("over",s,this._uiHash(this)),this.containers[m].containerCache.over=1);else{for(o=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],a=this.items.length-1;a>=0;a--)t.contains(this.containers[m].element[0],this.items[a].item[0])&&this.items[a].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[a].top,this.items[a].height))&&(u=this.items[a].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[a][l]-c)&&(d=!0,u+=this.items[a][l]),o>Math.abs(u-c)&&(o=Math.abs(u-c),r=this.items[a],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[m])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[m].element,!0),this._trigger("change",s,this._uiHash()),this.containers[m]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[m],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[m]._trigger("over",s,this._uiHash(this)),this.containers[m].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,a=e.pageX,o=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(a=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(o=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(a=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){this.reverting=!1;var i,s=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)("auto"===this._storedCSS[i]||"static"===this._storedCSS[i])&&(this._storedCSS[i]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&s.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||s.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(s.push(function(t){this._trigger("remove",t,this._uiHash())}),s.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),s.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;i>=0;i--)e||s.push(function(t){return function(e){t._trigger("deactivate",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over&&(s.push(function(t){return function(e){t._trigger("out",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery);(function(t){var e=0,i={},s={};i.height=i.paddingTop=i.paddingBottom=i.borderTopWidth=i.borderBottomWidth="hide",s.height=s.paddingTop=s.paddingBottom=s.borderTopWidth=s.borderBottomWidth="show",t.widget("ui.accordion",{version:"1.10.3",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},_create:function(){var e=this.options;this.prevShow=this.prevHide=t(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),e.collapsible||e.active!==!1&&null!=e.active||(e.active=0),this._processPanels(),0>e.active&&(e.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():t(),content:this.active.length?this.active.next():t()}},_createIcons:function(){var e=this.options.icons;e&&(t("<span>").addClass("ui-accordion-header-icon ui-icon "+e.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(e.header).addClass(e.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){return"active"===t?(this._activate(e),undefined):("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||this.options.active!==!1||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons()),"disabled"===t&&this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!e),undefined)},_keydown:function(e){if(!e.altKey&&!e.ctrlKey){var i=t.ui.keyCode,s=this.headers.length,n=this.headers.index(e.target),a=!1;switch(e.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(e);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(t(e.target).attr("tabIndex",-1),t(a).attr("tabIndex",0),a.focus(),e.preventDefault())}},_panelKeyDown:function(e){e.keyCode===t.ui.keyCode.UP&&e.ctrlKey&&t(e.currentTarget).prev().focus()},refresh:function(){var e=this.options;this._processPanels(),e.active===!1&&e.collapsible===!0||!this.headers.length?(e.active=!1,this.active=t()):e.active===!1?this._activate(0):this.active.length&&!t.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(e.active=!1,this.active=t()):this._activate(Math.max(0,e.active-1)):e.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var i,s=this.options,n=s.heightStyle,a=this.element.parent(),o=this.accordionId="ui-accordion-"+(this.element.attr("id")||++e);this.active=this._findActive(s.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(e){var i=t(this),s=i.attr("id"),n=i.next(),a=n.attr("id");s||(s=o+"-header-"+e,i.attr("id",s)),a||(a=o+"-panel-"+e,n.attr("id",a)),i.attr("aria-controls",a),n.attr("aria-labelledby",s)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false",tabIndex:-1}).next().attr({"aria-expanded":"false","aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true",tabIndex:0}).next().attr({"aria-expanded":"true","aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(s.event),"fill"===n?(i=a.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.headers.each(function(){i-=t(this).outerHeight(!0)}),this.headers.next().each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===n&&(i=0,this.headers.next().each(function(){i=Math.max(i,t(this).css("height","").height())}).height(i))},_activate:function(e){var i=this._findActive(e)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return"number"==typeof e?this.headers.eq(e):t()},_setupEvents:function(e){var i={keydown:"_keydown"};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?t():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?t():n,newPanel:r};e.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",e,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?t():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(e){var i=e.newPanel,s=this.prevShow.length?this.prevShow:e.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,e):(s.hide(),i.show(),this._toggleComplete(e)),s.attr({"aria-expanded":"false","aria-hidden":"true"}),s.prev().attr("aria-selected","false"),i.length&&s.length?s.prev().attr("tabIndex",-1):i.length&&this.headers.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr({"aria-expanded":"true","aria-hidden":"false"}).prev().attr({"aria-selected":"true",tabIndex:0})},_animate:function(t,e,n){var a,o,r,h=this,l=0,c=t.length&&(!e.length||t.index()<e.index()),u=this.options.animate||{},d=c&&u.down||u,p=function(){h._toggleComplete(n)};return"number"==typeof d&&(r=d),"string"==typeof d&&(o=d),o=o||d.easing||u.easing,r=r||d.duration||u.duration,e.length?t.length?(a=t.show().outerHeight(),e.animate(i,{duration:r,easing:o,step:function(t,e){e.now=Math.round(t)}}),t.hide().animate(s,{duration:r,easing:o,complete:p,step:function(t,i){i.now=Math.round(t),"height"!==i.prop?l+=i.now:"content"!==h.options.heightStyle&&(i.now=Math.round(a-e.outerHeight()-l),l=0)}}),undefined):e.animate(i,r,o,p):t.animate(s,r,o,p)},_toggleComplete:function(t){var e=t.oldPanel;e.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),e.length&&(e.parent()[0].className=e.parent()[0].className),this._trigger("activate",null,t)}})})(jQuery);(function(t){var e=0;t.widget("ui.autocomplete",{version:"1.10.3",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,undefined;e=!1,s=!1,i=!1;var a=t.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:e=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case a.UP:e=!0,this._keyEvent("previous",n);break;case a.DOWN:e=!0,this._keyEvent("next",n);break;case a.ENTER:case a.NUMPAD_ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),undefined;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),undefined):(this._searchTimeout(t),undefined)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,undefined):(clearTimeout(this.searching),this.close(t),this._change(t),undefined)}}),this._initSource(),this.menu=t("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().data("ui-menu"),this._on(this.menu.element,{mousedown:function(e){e.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];t(e.target).closest(".ui-menu-item").length||this._delay(function(){var e=this;this.document.one("mousedown",function(s){s.target===e.element[0]||s.target===i||t.contains(i,s.target)||e.close()})})},menufocus:function(e,i){if(this.isNewMenu&&(this.isNewMenu=!1,e.originalEvent&&/^mouse/.test(e.originalEvent.type)))return this.menu.blur(),this.document.one("mousemove",function(){t(e.target).trigger(e.originalEvent)}),undefined;var s=i.item.data("ui-autocomplete-item");!1!==this._trigger("focus",e,{item:s})?e.originalEvent&&/^key/.test(e.originalEvent.type)&&this._value(s.value):this.liveRegion.text(s.value)},menuselect:function(t,e){var i=e.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",t,{item:i})&&this._value(i.value),this.term=this._value(),this.close(t),this.selectedItem=i}}),this.liveRegion=t("<span>",{role:"status","aria-live":"polite"}).addClass("ui-helper-hidden-accessible").insertBefore(this.element),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e||(e=this.element.closest(".ui-front")),e.length||(e=this.document[0].body),e},_initSource:function(){var e,i,s=this;t.isArray(this.options.source)?(e=this.options.source,this.source=function(i,s){s(t.ui.autocomplete.filter(e,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(e,n){s.xhr&&s.xhr.abort(),s.xhr=t.ajax({url:i,data:e,dataType:"json",success:function(t){n(t)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(t){clearTimeout(this.searching),this.searching=this._delay(function(){this.term!==this._value()&&(this.selectedItem=null,this.search(null,t))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length<this.options.minLength?this.close(e):this._trigger("search",e)!==!1?this._search(t):undefined},_search:function(t){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:t},this._response())},_response:function(){var t=this,i=++e;return function(s){i===e&&t.__response(s),t.pending--,t.pending||t.element.removeClass("ui-autocomplete-loading")}},__response:function(t){t&&(t=this._normalize(t)),this._trigger("response",null,{content:t}),!this.options.disabled&&t&&t.length&&!this.cancelSearch?(this._suggest(t),this._trigger("open")):this._close()},close:function(t){this.cancelSearch=!0,this._close(t)},_close:function(t){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",t))},_change:function(t){this.previous!==this._value()&&this._trigger("change",t,{item:this.selectedItem})},_normalize:function(e){return e.length&&e[0].label&&e[0].value?e:t.map(e,function(e){return"string"==typeof e?{label:e,value:e}:t.extend({label:e.label||e.value,value:e.value||e.label},e)})},_suggest:function(e){var i=this.menu.element.empty();this._renderMenu(i,e),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(t.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var t=this.menu.element;t.outerWidth(Math.max(t.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(e,i){var s=this;t.each(i,function(t,i){s._renderItemData(e,i)})},_renderItemData:function(t,e){return this._renderItem(t,e).data("ui-autocomplete-item",e)},_renderItem:function(e,i){return t("<li>").append(t("<a>").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this._value(this.term),this.menu.blur(),undefined):(this.menu[t](e),undefined):(this.search(null,e),undefined)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var e;this._superApply(arguments),this.options.disabled||this.cancelSearch||(e=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.text(e))}})})(jQuery);(function(t){var e,i,s,n,a="ui-button ui-widget ui-state-default ui-corner-all",o="ui-state-hover ui-state-active ",r="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",h=function(){var e=t(this);setTimeout(function(){e.find(":ui-button").button("refresh")},1)},l=function(e){var i=e.name,s=e.form,n=t([]);return i&&(i=i.replace(/'/g,"\\'"),n=s?t(s).find("[name='"+i+"']"):t("[name='"+i+"']",e.ownerDocument).filter(function(){return!this.form})),n};t.widget("ui.button",{version:"1.10.3",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,h),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var o=this,r=this.options,c="checkbox"===this.type||"radio"===this.type,u=c?"":"ui-state-active",d="ui-state-focus";null===r.label&&(r.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(a).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){r.disabled||this===e&&t(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){r.disabled||t(this).removeClass(u)}).bind("click"+this.eventNamespace,function(t){r.disabled&&(t.preventDefault(),t.stopImmediatePropagation())}),this.element.bind("focus"+this.eventNamespace,function(){o.buttonElement.addClass(d)}).bind("blur"+this.eventNamespace,function(){o.buttonElement.removeClass(d)}),c&&(this.element.bind("change"+this.eventNamespace,function(){n||o.refresh()}),this.buttonElement.bind("mousedown"+this.eventNamespace,function(t){r.disabled||(n=!1,i=t.pageX,s=t.pageY)}).bind("mouseup"+this.eventNamespace,function(t){r.disabled||(i!==t.pageX||s!==t.pageY)&&(n=!0)})),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return r.disabled||n?!1:undefined}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(r.disabled||n)return!1;t(this).addClass("ui-state-active"),o.buttonElement.attr("aria-pressed","true");var e=o.element[0];l(e).not(e).map(function(){return t(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return r.disabled?!1:(t(this).addClass("ui-state-active"),e=this,o.document.one("mouseup",function(){e=null}),undefined)}).bind("mouseup"+this.eventNamespace,function(){return r.disabled?!1:(t(this).removeClass("ui-state-active"),undefined)}).bind("keydown"+this.eventNamespace,function(e){return r.disabled?!1:((e.keyCode===t.ui.keyCode.SPACE||e.keyCode===t.ui.keyCode.ENTER)&&t(this).addClass("ui-state-active"),undefined)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){t(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(e){e.keyCode===t.ui.keyCode.SPACE&&t(this).click()})),this._setOption("disabled",r.disabled),this._resetButton()},_determineButtonType:function(){var t,e,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(t=this.element.parents().last(),e="label[for='"+this.element.attr("id")+"']",this.buttonElement=t.find(e),this.buttonElement.length||(t=t.length?t.siblings():this.element.siblings(),this.buttonElement=t.filter(e),this.buttonElement.length||(this.buttonElement=t.find(e))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(a+" "+o+" "+r).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(t,e){return this._super(t,e),"disabled"===t?(e?this.element.prop("disabled",!0):this.element.prop("disabled",!1),undefined):(this._resetButton(),undefined)},refresh:function(){var e=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");e!==this.options.disabled&&this._setOption("disabled",e),"radio"===this.type?l(this.element[0]).each(function(){t(this).is(":checked")?t(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):t(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),undefined;var e=this.buttonElement.removeClass(r),i=t("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(e.empty()).text(),s=this.options.icons,n=s.primary&&s.secondary,a=[];s.primary||s.secondary?(this.options.text&&a.push("ui-button-text-icon"+(n?"s":s.primary?"-primary":"-secondary")),s.primary&&e.prepend("<span class='ui-button-icon-primary ui-icon "+s.primary+"'></span>"),s.secondary&&e.append("<span class='ui-button-icon-secondary ui-icon "+s.secondary+"'></span>"),this.options.text||(a.push(n?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||e.attr("title",t.trim(i)))):a.push("ui-button-text-only"),e.addClass(a.join(" "))}}),t.widget("ui.buttonset",{version:"1.10.3",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(t,e){"disabled"===t&&this.buttons.button("option",t,e),this._super(t,e)},refresh:function(){var e="rtl"===this.element.css("direction");this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return t(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(e?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(e?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return t(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}})})(jQuery);(function(t,e){function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.dpDiv=s(t("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function s(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.delegate(i,"mouseout",function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){t.datepicker._isDisabledDatepicker(a.inline?e.parent()[0]:a.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))})}function n(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}t.extend(t.ui,{datepicker:{version:"1.10.3"}});var a,r="datepicker";t.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(t){return n(this._defaults,t||{}),this},_attachDatepicker:function(e,i){var s,n,a;s=e.nodeName.toLowerCase(),n="div"===s||"span"===s,e.id||(this.uuid+=1,e.id="dp"+this.uuid),a=this._newInst(t(e),n),a.settings=t.extend({},i||{}),"input"===s?this._connectDatepicker(e,a):n&&this._inlineDatepicker(e,a)},_newInst:function(e,i){var n=e[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:n,input:e,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?s(t("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(e,i){var s=t(e);i.append=t([]),i.trigger=t([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),t.data(e,r,i),i.settings.disabled&&this._disableDatepicker(e))},_attachments:function(e,i){var s,n,a,r=this._get(i,"appendText"),o=this._get(i,"isRTL");i.append&&i.append.remove(),r&&(i.append=t("<span class='"+this._appendClass+"'>"+r+"</span>"),e[o?"before":"after"](i.append)),e.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&e.focus(this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),a=this._get(i,"buttonImage"),i.trigger=t(this._get(i,"buttonImageOnly")?t("<img/>").addClass(this._triggerClass).attr({src:a,alt:n,title:n}):t("<button type='button'></button>").addClass(this._triggerClass).html(a?t("<img/>").attr({src:a,alt:n,title:n}):n)),e[o?"before":"after"](i.trigger),i.trigger.click(function(){return t.datepicker._datepickerShowing&&t.datepicker._lastInput===e[0]?t.datepicker._hideDatepicker():t.datepicker._datepickerShowing&&t.datepicker._lastInput!==e[0]?(t.datepicker._hideDatepicker(),t.datepicker._showDatepicker(e[0])):t.datepicker._showDatepicker(e[0]),!1}))},_autoSize:function(t){if(this._get(t,"autoSize")&&!t.inline){var e,i,s,n,a=new Date(2009,11,20),r=this._get(t,"dateFormat");r.match(/[DM]/)&&(e=function(t){for(i=0,s=0,n=0;t.length>n;n++)t[n].length>i&&(i=t[n].length,s=n);return s},a.setMonth(e(this._get(t,r.match(/MM/)?"monthNames":"monthNamesShort"))),a.setDate(e(this._get(t,r.match(/DD/)?"dayNames":"dayNamesShort"))+20-a.getDay())),t.input.attr("size",this._formatDate(t,a).length)}},_inlineDatepicker:function(e,i){var s=t(e);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),t.data(e,r,i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(e),i.dpDiv.css("display","block"))},_dialogDatepicker:function(e,i,s,a,o){var h,l,c,u,d,p=this._dialogInst;return p||(this.uuid+=1,h="dp"+this.uuid,this._dialogInput=t("<input type='text' id='"+h+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),t("body").append(this._dialogInput),p=this._dialogInst=this._newInst(this._dialogInput,!1),p.settings={},t.data(this._dialogInput[0],r,p)),n(p.settings,a||{}),i=i&&i.constructor===Date?this._formatDate(p,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(l=document.documentElement.clientWidth,c=document.documentElement.clientHeight,u=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[l/2-100+u,c/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),p.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),t.blockUI&&t.blockUI(this.dpDiv),t.data(this._dialogInput[0],r,p),this},_destroyDatepicker:function(e){var i,s=t(e),n=t.data(e,r);s.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),t.removeData(e,r),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty())},_enableDatepicker:function(e){var i,s,n=t(e),a=t.data(e,r);n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!1,a.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}))},_disableDatepicker:function(e){var i,s,n=t(e),a=t.data(e,r);n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!0,a.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}),this._disabledInputs[this._disabledInputs.length]=e)},_isDisabledDatepicker:function(t){if(!t)return!1;for(var e=0;this._disabledInputs.length>e;e++)if(this._disabledInputs[e]===t)return!0;return!1},_getInst:function(e){try{return t.data(e,r)}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(i,s,a){var r,o,h,l,c=this._getInst(i);return 2===arguments.length&&"string"==typeof s?"defaults"===s?t.extend({},t.datepicker._defaults):c?"all"===s?t.extend({},c.settings):this._get(c,s):null:(r=s||{},"string"==typeof s&&(r={},r[s]=a),c&&(this._curInst===c&&this._hideDatepicker(),o=this._getDateDatepicker(i,!0),h=this._getMinMaxDate(c,"min"),l=this._getMinMaxDate(c,"max"),n(c.settings,r),null!==h&&r.dateFormat!==e&&r.minDate===e&&(c.settings.minDate=this._formatDate(c,h)),null!==l&&r.dateFormat!==e&&r.maxDate===e&&(c.settings.maxDate=this._formatDate(c,l)),"disabled"in r&&(r.disabled?this._disableDatepicker(i):this._enableDatepicker(i)),this._attachments(t(i),c),this._autoSize(c),this._setDate(c,o),this._updateAlternate(c),this._updateDatepicker(c)),e)},_changeDatepicker:function(t,e,i){this._optionDatepicker(t,e,i)},_refreshDatepicker:function(t){var e=this._getInst(t);e&&this._updateDatepicker(e)},_setDateDatepicker:function(t,e){var i=this._getInst(t);i&&(this._setDate(i,e),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(t,e){var i=this._getInst(t);return i&&!i.inline&&this._setDateFromField(i,e),i?this._getDate(i):null},_doKeyDown:function(e){var i,s,n,a=t.datepicker._getInst(e.target),r=!0,o=a.dpDiv.is(".ui-datepicker-rtl");if(a._keyEvent=!0,t.datepicker._datepickerShowing)switch(e.keyCode){case 9:t.datepicker._hideDatepicker(),r=!1;break;case 13:return n=t("td."+t.datepicker._dayOverClass+":not(."+t.datepicker._currentClass+")",a.dpDiv),n[0]&&t.datepicker._selectDay(e.target,a.selectedMonth,a.selectedYear,n[0]),i=t.datepicker._get(a,"onSelect"),i?(s=t.datepicker._formatDate(a),i.apply(a.input?a.input[0]:null,[s,a])):t.datepicker._hideDatepicker(),!1;case 27:t.datepicker._hideDatepicker();break;case 33:t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(a,"stepBigMonths"):-t.datepicker._get(a,"stepMonths"),"M");break;case 34:t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(a,"stepBigMonths"):+t.datepicker._get(a,"stepMonths"),"M");break;case 35:(e.ctrlKey||e.metaKey)&&t.datepicker._clearDate(e.target),r=e.ctrlKey||e.metaKey;break;case 36:(e.ctrlKey||e.metaKey)&&t.datepicker._gotoToday(e.target),r=e.ctrlKey||e.metaKey;break;case 37:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,o?1:-1,"D"),r=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(a,"stepBigMonths"):-t.datepicker._get(a,"stepMonths"),"M");break;case 38:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,-7,"D"),r=e.ctrlKey||e.metaKey;break;case 39:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,o?-1:1,"D"),r=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(a,"stepBigMonths"):+t.datepicker._get(a,"stepMonths"),"M");break;case 40:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,7,"D"),r=e.ctrlKey||e.metaKey;break;default:r=!1}else 36===e.keyCode&&e.ctrlKey?t.datepicker._showDatepicker(this):r=!1;r&&(e.preventDefault(),e.stopPropagation())},_doKeyPress:function(i){var s,n,a=t.datepicker._getInst(i.target);return t.datepicker._get(a,"constrainInput")?(s=t.datepicker._possibleChars(t.datepicker._get(a,"dateFormat")),n=String.fromCharCode(null==i.charCode?i.keyCode:i.charCode),i.ctrlKey||i.metaKey||" ">n||!s||s.indexOf(n)>-1):e},_doKeyUp:function(e){var i,s=t.datepicker._getInst(e.target);if(s.input.val()!==s.lastVal)try{i=t.datepicker.parseDate(t.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,t.datepicker._getFormatConfig(s)),i&&(t.datepicker._setDateFromField(s),t.datepicker._updateAlternate(s),t.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(e){if(e=e.target||e,"input"!==e.nodeName.toLowerCase()&&(e=t("input",e.parentNode)[0]),!t.datepicker._isDisabledDatepicker(e)&&t.datepicker._lastInput!==e){var i,s,a,r,o,h,l;i=t.datepicker._getInst(e),t.datepicker._curInst&&t.datepicker._curInst!==i&&(t.datepicker._curInst.dpDiv.stop(!0,!0),i&&t.datepicker._datepickerShowing&&t.datepicker._hideDatepicker(t.datepicker._curInst.input[0])),s=t.datepicker._get(i,"beforeShow"),a=s?s.apply(e,[e,i]):{},a!==!1&&(n(i.settings,a),i.lastVal=null,t.datepicker._lastInput=e,t.datepicker._setDateFromField(i),t.datepicker._inDialog&&(e.value=""),t.datepicker._pos||(t.datepicker._pos=t.datepicker._findPos(e),t.datepicker._pos[1]+=e.offsetHeight),r=!1,t(e).parents().each(function(){return r|="fixed"===t(this).css("position"),!r}),o={left:t.datepicker._pos[0],top:t.datepicker._pos[1]},t.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),t.datepicker._updateDatepicker(i),o=t.datepicker._checkOffset(i,o,r),i.dpDiv.css({position:t.datepicker._inDialog&&t.blockUI?"static":r?"fixed":"absolute",display:"none",left:o.left+"px",top:o.top+"px"}),i.inline||(h=t.datepicker._get(i,"showAnim"),l=t.datepicker._get(i,"duration"),i.dpDiv.zIndex(t(e).zIndex()+1),t.datepicker._datepickerShowing=!0,t.effects&&t.effects.effect[h]?i.dpDiv.show(h,t.datepicker._get(i,"showOptions"),l):i.dpDiv[h||"show"](h?l:null),t.datepicker._shouldFocusInput(i)&&i.input.focus(),t.datepicker._curInst=i))}},_updateDatepicker:function(e){this.maxRows=4,a=e,e.dpDiv.empty().append(this._generateHTML(e)),this._attachHandlers(e),e.dpDiv.find("."+this._dayOverClass+" a").mouseover();var i,s=this._getNumberOfMonths(e),n=s[1],r=17;e.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&e.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",r*n+"em"),e.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),e.dpDiv[(this._get(e,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),e===t.datepicker._curInst&&t.datepicker._datepickerShowing&&t.datepicker._shouldFocusInput(e)&&e.input.focus(),e.yearshtml&&(i=e.yearshtml,setTimeout(function(){i===e.yearshtml&&e.yearshtml&&e.dpDiv.find("select.ui-datepicker-year:first").replaceWith(e.yearshtml),i=e.yearshtml=null},0))},_shouldFocusInput:function(t){return t.input&&t.input.is(":visible")&&!t.input.is(":disabled")&&!t.input.is(":focus")},_checkOffset:function(e,i,s){var n=e.dpDiv.outerWidth(),a=e.dpDiv.outerHeight(),r=e.input?e.input.outerWidth():0,o=e.input?e.input.outerHeight():0,h=document.documentElement.clientWidth+(s?0:t(document).scrollLeft()),l=document.documentElement.clientHeight+(s?0:t(document).scrollTop());return i.left-=this._get(e,"isRTL")?n-r:0,i.left-=s&&i.left===e.input.offset().left?t(document).scrollLeft():0,i.top-=s&&i.top===e.input.offset().top+o?t(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>h&&h>n?Math.abs(i.left+n-h):0),i.top-=Math.min(i.top,i.top+a>l&&l>a?Math.abs(a+o):0),i},_findPos:function(e){for(var i,s=this._getInst(e),n=this._get(s,"isRTL");e&&("hidden"===e.type||1!==e.nodeType||t.expr.filters.hidden(e));)e=e[n?"previousSibling":"nextSibling"];return i=t(e).offset(),[i.left,i.top]},_hideDatepicker:function(e){var i,s,n,a,o=this._curInst;!o||e&&o!==t.data(e,r)||this._datepickerShowing&&(i=this._get(o,"showAnim"),s=this._get(o,"duration"),n=function(){t.datepicker._tidyDialog(o)},t.effects&&(t.effects.effect[i]||t.effects[i])?o.dpDiv.hide(i,t.datepicker._get(o,"showOptions"),s,n):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,a=this._get(o,"onClose"),a&&a.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),t.blockUI&&(t.unblockUI(),t("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(t){t.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(e){if(t.datepicker._curInst){var i=t(e.target),s=t.datepicker._getInst(i[0]);(i[0].id!==t.datepicker._mainDivId&&0===i.parents("#"+t.datepicker._mainDivId).length&&!i.hasClass(t.datepicker.markerClassName)&&!i.closest("."+t.datepicker._triggerClass).length&&t.datepicker._datepickerShowing&&(!t.datepicker._inDialog||!t.blockUI)||i.hasClass(t.datepicker.markerClassName)&&t.datepicker._curInst!==s)&&t.datepicker._hideDatepicker()}},_adjustDate:function(e,i,s){var n=t(e),a=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(a,i+("M"===s?this._get(a,"showCurrentAtPos"):0),s),this._updateDatepicker(a))},_gotoToday:function(e){var i,s=t(e),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(e,i,s){var n=t(e),a=this._getInst(n[0]);a["selected"+("M"===s?"Month":"Year")]=a["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(a),this._adjustDate(n)},_selectDay:function(e,i,s,n){var a,r=t(e);t(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(r[0])||(a=this._getInst(r[0]),a.selectedDay=a.currentDay=t("a",n).html(),a.selectedMonth=a.currentMonth=i,a.selectedYear=a.currentYear=s,this._selectDate(e,this._formatDate(a,a.currentDay,a.currentMonth,a.currentYear)))},_clearDate:function(e){var i=t(e);this._selectDate(i,"")},_selectDate:function(e,i){var s,n=t(e),a=this._getInst(n[0]);i=null!=i?i:this._formatDate(a),a.input&&a.input.val(i),this._updateAlternate(a),s=this._get(a,"onSelect"),s?s.apply(a.input?a.input[0]:null,[i,a]):a.input&&a.input.trigger("change"),a.inline?this._updateDatepicker(a):(this._hideDatepicker(),this._lastInput=a.input[0],"object"!=typeof a.input[0]&&a.input.focus(),this._lastInput=null)},_updateAlternate:function(e){var i,s,n,a=this._get(e,"altField");a&&(i=this._get(e,"altFormat")||this._get(e,"dateFormat"),s=this._getDate(e),n=this.formatDate(i,s,this._getFormatConfig(e)),t(a).each(function(){t(this).val(n)}))},noWeekends:function(t){var e=t.getDay();return[e>0&&6>e,""]},iso8601Week:function(t){var e,i=new Date(t.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),e=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((e-i)/864e5)/7)+1},parseDate:function(i,s,n){if(null==i||null==s)throw"Invalid arguments";if(s="object"==typeof s?""+s:s+"",""===s)return null;var a,r,o,h,l=0,c=(n?n.shortYearCutoff:null)||this._defaults.shortYearCutoff,u="string"!=typeof c?c:(new Date).getFullYear()%100+parseInt(c,10),d=(n?n.dayNamesShort:null)||this._defaults.dayNamesShort,p=(n?n.dayNames:null)||this._defaults.dayNames,f=(n?n.monthNamesShort:null)||this._defaults.monthNamesShort,m=(n?n.monthNames:null)||this._defaults.monthNames,g=-1,v=-1,_=-1,b=-1,y=!1,x=function(t){var e=i.length>a+1&&i.charAt(a+1)===t;return e&&a++,e},k=function(t){var e=x(t),i="@"===t?14:"!"===t?20:"y"===t&&e?4:"o"===t?3:2,n=RegExp("^\\d{1,"+i+"}"),a=s.substring(l).match(n);if(!a)throw"Missing number at position "+l;return l+=a[0].length,parseInt(a[0],10)},w=function(i,n,a){var r=-1,o=t.map(x(i)?a:n,function(t,e){return[[e,t]]}).sort(function(t,e){return-(t[1].length-e[1].length)});if(t.each(o,function(t,i){var n=i[1];return s.substr(l,n.length).toLowerCase()===n.toLowerCase()?(r=i[0],l+=n.length,!1):e}),-1!==r)return r+1;throw"Unknown name at position "+l},D=function(){if(s.charAt(l)!==i.charAt(a))throw"Unexpected literal at position "+l;l++};for(a=0;i.length>a;a++)if(y)"'"!==i.charAt(a)||x("'")?D():y=!1;else switch(i.charAt(a)){case"d":_=k("d");break;case"D":w("D",d,p);break;case"o":b=k("o");break;case"m":v=k("m");break;case"M":v=w("M",f,m);break;case"y":g=k("y");break;case"@":h=new Date(k("@")),g=h.getFullYear(),v=h.getMonth()+1,_=h.getDate();break;case"!":h=new Date((k("!")-this._ticksTo1970)/1e4),g=h.getFullYear(),v=h.getMonth()+1,_=h.getDate();break;case"'":x("'")?D():y=!0;break;default:D()}if(s.length>l&&(o=s.substr(l),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===g?g=(new Date).getFullYear():100>g&&(g+=(new Date).getFullYear()-(new Date).getFullYear()%100+(u>=g?0:-100)),b>-1)for(v=1,_=b;;){if(r=this._getDaysInMonth(g,v-1),r>=_)break;v++,_-=r}if(h=this._daylightSavingAdjust(new Date(g,v-1,_)),h.getFullYear()!==g||h.getMonth()+1!==v||h.getDate()!==_)throw"Invalid date";return h},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(t,e,i){if(!e)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,a=(i?i.dayNames:null)||this._defaults.dayNames,r=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,o=(i?i.monthNames:null)||this._defaults.monthNames,h=function(e){var i=t.length>s+1&&t.charAt(s+1)===e;return i&&s++,i},l=function(t,e,i){var s=""+e;if(h(t))for(;i>s.length;)s="0"+s;return s},c=function(t,e,i,s){return h(t)?s[e]:i[e]},u="",d=!1;if(e)for(s=0;t.length>s;s++)if(d)"'"!==t.charAt(s)||h("'")?u+=t.charAt(s):d=!1;else switch(t.charAt(s)){case"d":u+=l("d",e.getDate(),2);break;case"D":u+=c("D",e.getDay(),n,a);break;case"o":u+=l("o",Math.round((new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()-new Date(e.getFullYear(),0,0).getTime())/864e5),3);break;case"m":u+=l("m",e.getMonth()+1,2);break;case"M":u+=c("M",e.getMonth(),r,o);break;case"y":u+=h("y")?e.getFullYear():(10>e.getYear()%100?"0":"")+e.getYear()%100;break;case"@":u+=e.getTime();break;case"!":u+=1e4*e.getTime()+this._ticksTo1970;break;case"'":h("'")?u+="'":d=!0;break;default:u+=t.charAt(s)}return u},_possibleChars:function(t){var e,i="",s=!1,n=function(i){var s=t.length>e+1&&t.charAt(e+1)===i;return s&&e++,s};for(e=0;t.length>e;e++)if(s)"'"!==t.charAt(e)||n("'")?i+=t.charAt(e):s=!1;else switch(t.charAt(e)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=t.charAt(e)}return i},_get:function(t,i){return t.settings[i]!==e?t.settings[i]:this._defaults[i]},_setDateFromField:function(t,e){if(t.input.val()!==t.lastVal){var i=this._get(t,"dateFormat"),s=t.lastVal=t.input?t.input.val():null,n=this._getDefaultDate(t),a=n,r=this._getFormatConfig(t);try{a=this.parseDate(i,s,r)||n}catch(o){s=e?"":s}t.selectedDay=a.getDate(),t.drawMonth=t.selectedMonth=a.getMonth(),t.drawYear=t.selectedYear=a.getFullYear(),t.currentDay=s?a.getDate():0,t.currentMonth=s?a.getMonth():0,t.currentYear=s?a.getFullYear():0,this._adjustInstDate(t)}},_getDefaultDate:function(t){return this._restrictMinMax(t,this._determineDate(t,this._get(t,"defaultDate"),new Date))},_determineDate:function(e,i,s){var n=function(t){var e=new Date;return e.setDate(e.getDate()+t),e},a=function(i){try{return t.datepicker.parseDate(t.datepicker._get(e,"dateFormat"),i,t.datepicker._getFormatConfig(e))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?t.datepicker._getDate(e):null)||new Date,a=n.getFullYear(),r=n.getMonth(),o=n.getDate(),h=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,l=h.exec(i);l;){switch(l[2]||"d"){case"d":case"D":o+=parseInt(l[1],10);break;case"w":case"W":o+=7*parseInt(l[1],10);break;case"m":case"M":r+=parseInt(l[1],10),o=Math.min(o,t.datepicker._getDaysInMonth(a,r));break;case"y":case"Y":a+=parseInt(l[1],10),o=Math.min(o,t.datepicker._getDaysInMonth(a,r))}l=h.exec(i)}return new Date(a,r,o)},r=null==i||""===i?s:"string"==typeof i?a(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return r=r&&"Invalid Date"==""+r?s:r,r&&(r.setHours(0),r.setMinutes(0),r.setSeconds(0),r.setMilliseconds(0)),this._daylightSavingAdjust(r)},_daylightSavingAdjust:function(t){return t?(t.setHours(t.getHours()>12?t.getHours()+2:0),t):null},_setDate:function(t,e,i){var s=!e,n=t.selectedMonth,a=t.selectedYear,r=this._restrictMinMax(t,this._determineDate(t,e,new Date));t.selectedDay=t.currentDay=r.getDate(),t.drawMonth=t.selectedMonth=t.currentMonth=r.getMonth(),t.drawYear=t.selectedYear=t.currentYear=r.getFullYear(),n===t.selectedMonth&&a===t.selectedYear||i||this._notifyChange(t),this._adjustInstDate(t),t.input&&t.input.val(s?"":this._formatDate(t))},_getDate:function(t){var e=!t.currentYear||t.input&&""===t.input.val()?null:this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return e},_attachHandlers:function(e){var i=this._get(e,"stepMonths"),s="#"+e.id.replace(/\\\\/g,"\\");e.dpDiv.find("[data-handler]").map(function(){var e={prev:function(){t.datepicker._adjustDate(s,-i,"M")},next:function(){t.datepicker._adjustDate(s,+i,"M")},hide:function(){t.datepicker._hideDatepicker()},today:function(){t.datepicker._gotoToday(s)},selectDay:function(){return t.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return t.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return t.datepicker._selectMonthYear(s,this,"Y"),!1}};t(this).bind(this.getAttribute("data-event"),e[this.getAttribute("data-handler")])})},_generateHTML:function(t){var e,i,s,n,a,r,o,h,l,c,u,d,p,f,m,g,v,_,b,y,x,k,w,D,T,C,M,S,N,I,P,A,z,H,E,F,O,W,j,R=new Date,L=this._daylightSavingAdjust(new Date(R.getFullYear(),R.getMonth(),R.getDate())),Y=this._get(t,"isRTL"),B=this._get(t,"showButtonPanel"),J=this._get(t,"hideIfNoPrevNext"),K=this._get(t,"navigationAsDateFormat"),Q=this._getNumberOfMonths(t),V=this._get(t,"showCurrentAtPos"),U=this._get(t,"stepMonths"),q=1!==Q[0]||1!==Q[1],X=this._daylightSavingAdjust(t.currentDay?new Date(t.currentYear,t.currentMonth,t.currentDay):new Date(9999,9,9)),G=this._getMinMaxDate(t,"min"),$=this._getMinMaxDate(t,"max"),Z=t.drawMonth-V,te=t.drawYear;if(0>Z&&(Z+=12,te--),$)for(e=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-Q[0]*Q[1]+1,$.getDate())),e=G&&G>e?G:e;this._daylightSavingAdjust(new Date(te,Z,1))>e;)Z--,0>Z&&(Z=11,te--);for(t.drawMonth=Z,t.drawYear=te,i=this._get(t,"prevText"),i=K?this.formatDate(i,this._daylightSavingAdjust(new Date(te,Z-U,1)),this._getFormatConfig(t)):i,s=this._canAdjustMonth(t,-1,te,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>":J?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>",n=this._get(t,"nextText"),n=K?this.formatDate(n,this._daylightSavingAdjust(new Date(te,Z+U,1)),this._getFormatConfig(t)):n,a=this._canAdjustMonth(t,1,te,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>":J?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>",r=this._get(t,"currentText"),o=this._get(t,"gotoCurrent")&&t.currentDay?X:L,r=K?this.formatDate(r,o,this._getFormatConfig(t)):r,h=t.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(t,"closeText")+"</button>",l=B?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Y?h:"")+(this._isInRange(t,o)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+r+"</button>":"")+(Y?"":h)+"</div>":"",c=parseInt(this._get(t,"firstDay"),10),c=isNaN(c)?0:c,u=this._get(t,"showWeek"),d=this._get(t,"dayNames"),p=this._get(t,"dayNamesMin"),f=this._get(t,"monthNames"),m=this._get(t,"monthNamesShort"),g=this._get(t,"beforeShowDay"),v=this._get(t,"showOtherMonths"),_=this._get(t,"selectOtherMonths"),b=this._getDefaultDate(t),y="",k=0;Q[0]>k;k++){for(w="",this.maxRows=4,D=0;Q[1]>D;D++){if(T=this._daylightSavingAdjust(new Date(te,Z,t.selectedDay)),C=" ui-corner-all",M="",q){if(M+="<div class='ui-datepicker-group",Q[1]>1)switch(D){case 0:M+=" ui-datepicker-group-first",C=" ui-corner-"+(Y?"right":"left");break;case Q[1]-1:M+=" ui-datepicker-group-last",C=" ui-corner-"+(Y?"left":"right");break;default:M+=" ui-datepicker-group-middle",C=""}M+="'>"}for(M+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+C+"'>"+(/all|left/.test(C)&&0===k?Y?a:s:"")+(/all|right/.test(C)&&0===k?Y?s:a:"")+this._generateMonthYearHeader(t,Z,te,G,$,k>0||D>0,f,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",S=u?"<th class='ui-datepicker-week-col'>"+this._get(t,"weekHeader")+"</th>":"",x=0;7>x;x++)N=(x+c)%7,S+="<th"+((x+c+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+d[N]+"'>"+p[N]+"</span></th>";for(M+=S+"</tr></thead><tbody>",I=this._getDaysInMonth(te,Z),te===t.selectedYear&&Z===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,I)),P=(this._getFirstDayOfMonth(te,Z)-c+7)%7,A=Math.ceil((P+I)/7),z=q?this.maxRows>A?this.maxRows:A:A,this.maxRows=z,H=this._daylightSavingAdjust(new Date(te,Z,1-P)),E=0;z>E;E++){for(M+="<tr>",F=u?"<td class='ui-datepicker-week-col'>"+this._get(t,"calculateWeek")(H)+"</td>":"",x=0;7>x;x++)O=g?g.apply(t.input?t.input[0]:null,[H]):[!0,""],W=H.getMonth()!==Z,j=W&&!_||!O[0]||G&&G>H||$&&H>$,F+="<td class='"+((x+c+6)%7>=5?" ui-datepicker-week-end":"")+(W?" ui-datepicker-other-month":"")+(H.getTime()===T.getTime()&&Z===t.selectedMonth&&t._keyEvent||b.getTime()===H.getTime()&&b.getTime()===T.getTime()?" "+this._dayOverClass:"")+(j?" "+this._unselectableClass+" ui-state-disabled":"")+(W&&!v?"":" "+O[1]+(H.getTime()===X.getTime()?" "+this._currentClass:"")+(H.getTime()===L.getTime()?" ui-datepicker-today":""))+"'"+(W&&!v||!O[2]?"":" title='"+O[2].replace(/'/g,"'")+"'")+(j?"":" data-handler='selectDay' data-event='click' data-month='"+H.getMonth()+"' data-year='"+H.getFullYear()+"'")+">"+(W&&!v?" ":j?"<span class='ui-state-default'>"+H.getDate()+"</span>":"<a class='ui-state-default"+(H.getTime()===L.getTime()?" ui-state-highlight":"")+(H.getTime()===X.getTime()?" ui-state-active":"")+(W?" ui-priority-secondary":"")+"' href='#'>"+H.getDate()+"</a>")+"</td>",H.setDate(H.getDate()+1),H=this._daylightSavingAdjust(H);M+=F+"</tr>"}Z++,Z>11&&(Z=0,te++),M+="</tbody></table>"+(q?"</div>"+(Q[0]>0&&D===Q[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),w+=M}y+=w}return y+=l,t._keyEvent=!1,y},_generateMonthYearHeader:function(t,e,i,s,n,a,r,o){var h,l,c,u,d,p,f,m,g=this._get(t,"changeMonth"),v=this._get(t,"changeYear"),_=this._get(t,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",y="";if(a||!g)y+="<span class='ui-datepicker-month'>"+r[e]+"</span>";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,y+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",c=0;12>c;c++)(!h||c>=s.getMonth())&&(!l||n.getMonth()>=c)&&(y+="<option value='"+c+"'"+(c===e?" selected='selected'":"")+">"+o[c]+"</option>");y+="</select>"}if(_||(b+=y+(!a&&g&&v?"":" ")),!t.yearshtml)if(t.yearshtml="",a||!v)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(u=this._get(t,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(t){var e=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?d+parseInt(t,10):parseInt(t,10); +return isNaN(e)?d:e},f=p(u[0]),m=Math.max(f,p(u[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,t.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=f;f++)t.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";t.yearshtml+="</select>",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),_&&(b+=(!a&&g&&v?"":" ")+y),b+="</div>"},_adjustInstDate:function(t,e,i){var s=t.drawYear+("Y"===i?e:0),n=t.drawMonth+("M"===i?e:0),a=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),r=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,a)));t.selectedDay=r.getDate(),t.drawMonth=t.selectedMonth=r.getMonth(),t.drawYear=t.selectedYear=r.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=i&&i>e?i:e;return s&&n>s?s:n},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){var e=this._get(t,"numberOfMonths");return null==e?[1,1]:"number"==typeof e?[1,e]:e},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),a=this._daylightSavingAdjust(new Date(i,s+(0>e?e:n[0]*n[1]),1));return 0>e&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(t,a)},_isInRange:function(t,e){var i,s,n=this._getMinMaxDate(t,"min"),a=this._getMinMaxDate(t,"max"),r=null,o=null,h=this._get(t,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),r=parseInt(i[0],10),o=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(r+=s),i[1].match(/[+\-].*/)&&(o+=s)),(!n||e.getTime()>=n.getTime())&&(!a||e.getTime()<=a.getTime())&&(!r||e.getFullYear()>=r)&&(!o||o>=e.getFullYear())},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),{shortYearCutoff:e,dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);var n=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),n,this._getFormatConfig(t))}}),t.fn.datepicker=function(e){if(!this.length)return this;t.datepicker.initialized||(t(document).mousedown(t.datepicker._checkExternalClick),t.datepicker.initialized=!0),0===t("#"+t.datepicker._mainDivId).length&&t("body").append(t.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"isDisabled"!==e&&"getDate"!==e&&"widget"!==e?"option"===e&&2===arguments.length&&"string"==typeof arguments[1]?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof e?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this].concat(i)):t.datepicker._attachDatepicker(this,e)}):t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i))},t.datepicker=new i,t.datepicker.initialized=!1,t.datepicker.uuid=(new Date).getTime(),t.datepicker.version="1.10.3"})(jQuery);(function(t){var e={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},i={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0};t.widget("ui.dialog",{version:"1.10.3",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(e){var i=t(this).css(e).offset().top;0>i&&t(this).css("top",e.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&t.fn.draggable&&this._makeDraggable(),this.options.resizable&&t.fn.resizable&&this._makeResizable(),this._isOpen=!1},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var e=this.options.appendTo;return e&&(e.jquery||e.nodeType)?t(e):this.document.find(e||"body").eq(0)},_destroy:function(){var t,e=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),t=e.parent.children().eq(e.index),t.length&&t[0]!==this.element[0]?t.before(this.element):e.parent.append(this.element)},widget:function(){return this.uiDialog},disable:t.noop,enable:t.noop,close:function(e){var i=this;this._isOpen&&this._trigger("beforeClose",e)!==!1&&(this._isOpen=!1,this._destroyOverlay(),this.opener.filter(":focusable").focus().length||t(this.document[0].activeElement).blur(),this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",e)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,e){var i=!!this.uiDialog.nextAll(":visible").insertBefore(this.uiDialog).length;return i&&!e&&this._trigger("focus",t),i},open:function(){var e=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),undefined):(this._isOpen=!0,this.opener=t(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this._show(this.uiDialog,this.options.show,function(){e._focusTabbable(),e._trigger("focus")}),this._trigger("open"),undefined)},_focusTabbable:function(){var t=this.element.find("[autofocus]");t.length||(t=this.element.find(":tabbable")),t.length||(t=this.uiDialogButtonPane.find(":tabbable")),t.length||(t=this.uiDialogTitlebarClose.filter(":tabbable")),t.length||(t=this.uiDialog),t.eq(0).focus()},_keepFocus:function(e){function i(){var e=this.document[0].activeElement,i=this.uiDialog[0]===e||t.contains(this.uiDialog[0],e);i||this._focusTabbable()}e.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=t("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(e){if(this.options.closeOnEscape&&!e.isDefaultPrevented()&&e.keyCode&&e.keyCode===t.ui.keyCode.ESCAPE)return e.preventDefault(),this.close(e),undefined;if(e.keyCode===t.ui.keyCode.TAB){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");e.target!==n[0]&&e.target!==this.uiDialog[0]||e.shiftKey?e.target!==s[0]&&e.target!==this.uiDialog[0]||!e.shiftKey||(n.focus(1),e.preventDefault()):(s.focus(1),e.preventDefault())}},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var e;this.uiDialogTitlebar=t("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(e){t(e.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=t("<button></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(e),this.uiDialog.attr({"aria-labelledby":e.attr("id")})},_title:function(t){this.options.title||t.html(" "),t.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=t("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=t("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),undefined):(t.each(i,function(i,s){var n,a;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:"button"},s),n=s.click,s.click=function(){n.apply(e.element[0],arguments)},a={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,t("<button></button>",s).button(a).appendTo(e.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),undefined)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){t(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,e(n))},drag:function(t,s){i._trigger("drag",t,e(s))},stop:function(n,a){s.position=[a.position.left-i.document.scrollLeft(),a.position.top-i.document.scrollTop()],t(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,e(a))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,a=this.uiDialog.css("position"),o="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:o,start:function(s,n){t(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,e(n))},resize:function(t,s){i._trigger("resize",t,e(s))},stop:function(n,a){s.height=t(this).height(),s.width=t(this).width(),t(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,e(a))}}).css("position",a)},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(s){var n=this,a=!1,o={};t.each(s,function(t,s){n._setOption(t,s),t in e&&(a=!0),t in i&&(o[t]=s)}),a&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",o)},_setOption:function(t,e){var i,s,n=this.uiDialog;"dialogClass"===t&&n.removeClass(this.options.dialogClass).addClass(e),"disabled"!==t&&(this._super(t,e),"appendTo"===t&&this.uiDialog.appendTo(this._appendTo()),"buttons"===t&&this._createButtons(),"closeText"===t&&this.uiDialogTitlebarClose.button({label:""+e}),"draggable"===t&&(i=n.is(":data(ui-draggable)"),i&&!e&&n.draggable("destroy"),!i&&e&&this._makeDraggable()),"position"===t&&this._position(),"resizable"===t&&(s=n.is(":data(ui-resizable)"),s&&!e&&n.resizable("destroy"),s&&"string"==typeof e&&n.resizable("option","handles",e),s||e===!1||this._makeResizable()),"title"===t&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var e=t(this);return t("<div>").css({position:"absolute",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(".ui-dialog").length?!0:!!t(e.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var e=this,i=this.widgetFullName;t.ui.dialog.overlayInstances||this._delay(function(){t.ui.dialog.overlayInstances&&this.document.bind("focusin.dialog",function(s){e._allowInteraction(s)||(s.preventDefault(),t(".ui-dialog:visible:last .ui-dialog-content").data(i)._focusTabbable())})}),this.overlay=t("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),t.ui.dialog.overlayInstances++}},_destroyOverlay:function(){this.options.modal&&this.overlay&&(t.ui.dialog.overlayInstances--,t.ui.dialog.overlayInstances||this.document.unbind("focusin.dialog"),this.overlay.remove(),this.overlay=null)}}),t.ui.dialog.overlayInstances=0,t.uiBackCompat!==!1&&t.widget("ui.dialog",t.ui.dialog,{_position:function(){var e,i=this.options.position,s=[],n=[0,0];i?(("string"==typeof i||"object"==typeof i&&"0"in i)&&(s=i.split?i.split(" "):[i[0],i[1]],1===s.length&&(s[1]=s[0]),t.each(["left","top"],function(t,e){+s[t]===s[t]&&(n[t]=s[t],s[t]=e)}),i={my:s[0]+(0>n[0]?n[0]:"+"+n[0])+" "+s[1]+(0>n[1]?n[1]:"+"+n[1]),at:s.join(" ")}),i=t.extend({},t.ui.dialog.prototype.options.position,i)):i=t.ui.dialog.prototype.options.position,e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.position(i),e||this.uiDialog.hide()}})})(jQuery);(function(t){t.widget("ui.menu",{version:"1.10.3",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,t.proxy(function(t){this.options.disabled&&t.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(t){t.preventDefault()},"click .ui-state-disabled > a":function(t){t.preventDefault()},"click .ui-menu-item:has(a)":function(e){var i=t(e.target).closest(".ui-menu-item");!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.mouseHandled=!0,this.select(e),i.has(".ui-menu").length?this.expand(e):this.element.is(":focus")||(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){var i=t(e.currentTarget);i.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(e,i)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.children(".ui-menu-item").eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){t.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){t(e.target).closest(".ui-menu").length||this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var e=t(this);e.data("ui-menu-submenu-carat")&&e.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(e){function i(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var s,n,a,o,r,h=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:h=!1,n=this.previousFilter||"",a=String.fromCharCode(e.keyCode),o=!1,clearTimeout(this.filterTimer),a===n?o=!0:a=n+a,r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())}),s=o&&-1!==s.index(this.active.next())?this.active.nextAll(".ui-menu-item"):s,s.length||(a=String.fromCharCode(e.keyCode),r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())})),s.length?(this.focus(e,s),s.length>1?(this.previousFilter=a,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}h&&e.preventDefault()},_activate:function(t){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i=this.options.icons.submenu,s=this.element.find(this.options.menus);s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),s=e.prev("a"),n=t("<span>").addClass("ui-menu-icon ui-icon "+i).data("ui-menu-submenu-carat",!0);s.attr("aria-haspopup","true").prepend(n),e.attr("aria-labelledby",s.attr("id"))}),e=s.add(this.element),e.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),e.children(":not(.ui-menu-item)").each(function(){var e=t(this);/[^\-\u2014\u2013\s]/.test(e.text())||e.addClass("ui-widget-content ui-menu-divider")}),e.children(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){"icons"===t&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(e.submenu),this._super(t,e)},focus:function(t,e){var i,s;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=e.height(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",t,{item:this.active}))},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.children(".ui-menu-item")[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())),undefined):(this.next(e),undefined)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item").first())),undefined):(this.next(e),undefined)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(e){this.active=this.active||t(e.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(e,!0),this._trigger("select",e,i)}})})(jQuery);(function(t,e){t.widget("ui.progressbar",{version:"1.10.3",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=t("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(t){return t===e?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),e)},_constrainedValue:function(t){return t===e&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}})})(jQuery);(function(t){var e=5;t.widget("ui.slider",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)o.push(a);this.handles=n.add(t(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e)})},_createRange:function(){var e=this.options,i="";e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=t("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===e.range||"max"===e.range?" ui-slider-range-"+e.range:""))):this.range=t([])},_setupEvents:function(){var t=this.handles.add(this.range).filter("a");this._off(t),this._on(t,this._handleEvents),this._hoverable(t),this._focusable(t)},_destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,a,o,r,h,l,u=this,c=this.options;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-u.values(e));(n>i||n===i&&(e===u._lastChangedValue||u.values(e)===c.min))&&(n=i,a=t(this),o=e)}),r=this._start(e,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),h=a.offset(),l=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:e.pageX-h.left-a.width()/2,top:e.pageY-h.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,a;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(t,e){var i={handle:this.handles[e],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("start",t,i)},_slide:function(t,e,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(e?0:1),2===this.options.values.length&&this.options.range===!0&&(0===e&&i>s||1===e&&s>i)&&(i=s),i!==this.values(e)&&(n=this.values(),n[e]=i,a=this._trigger("slide",t,{handle:this.handles[e],value:i,values:n}),s=this.values(e?0:1),a!==!1&&this.values(e,i,!0))):i!==this.value()&&(a=this._trigger("slide",t,{handle:this.handles[e],value:i}),a!==!1&&this.value(i))},_stop:function(t,e){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("stop",t,i)},_change:function(t,e){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._lastChangedValue=e,this._trigger("change",t,i)}},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),undefined):this._value()},values:function(e,i){var s,n,a;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),undefined;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(e):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),t.Widget.prototype._setOption.apply(this,arguments),e){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var e,i,s,n,a,o=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),u["horizontal"===h.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[l?"animate":"css"](u,r.animate),h.options.range===!0&&("horizontal"===h.orientation?(0===s&&h.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[l?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[l?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(i){var s,n,a,o,r=t(i.target).data("ui-slider-handle-index");switch(i.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(i.preventDefault(),!this._keySliding&&(this._keySliding=!0,t(i.target).addClass("ui-state-active"),s=this._start(i,r),s===!1))return}switch(o=this.options.step,n=a=this.options.values&&this.options.values.length?this.values(r):this.value(),i.keyCode){case t.ui.keyCode.HOME:a=this._valueMin();break;case t.ui.keyCode.END:a=this._valueMax();break;case t.ui.keyCode.PAGE_UP:a=this._trimAlignValue(n+(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.PAGE_DOWN:a=this._trimAlignValue(n-(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(n===this._valueMax())return;a=this._trimAlignValue(n+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(n===this._valueMin())return;a=this._trimAlignValue(n-o)}this._slide(i,r,a)},click:function(t){t.preventDefault()},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),t(e.target).removeClass("ui-state-active"))}}})})(jQuery);(function(t){function e(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.widget("ui.spinner",{version:"1.10.3",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e={},i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);void 0!==n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var t=this.element[0]===this.document[0].activeElement;t||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var t=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=t.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*t.height())&&t.height()>0&&t.height(t.height()),this.options.disabled&&this.disable()},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>▲</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>▼</span>"+"</a>"},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){if("culture"===t||"numberFormat"===t){var i=this._parse(this.element.val());return this.options[t]=e,this.element.val(this._format(i)),void 0}("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(e.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(e.down)),this._super(t,e),"disabled"===t&&(e?(this.element.prop("disabled",!0),this.buttons.button("disable")):(this.element.prop("disabled",!1),this.buttons.button("enable")))},_setOptions:e(function(t){this._super(t),this._value(this.element.val())}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:e(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:e(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:e(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:e(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(e(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}})})(jQuery);(function(t,e){function i(){return++n}function s(t){return t.hash.length>1&&decodeURIComponent(t.href.replace(a,""))===decodeURIComponent(location.href.replace(a,""))}var n=0,a=/#.*$/;t.widget("ui.tabs",{version:"1.10.3",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var e=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var i=this.options.active,s=this.options.collapsible,n=location.hash.substring(1);return null===i&&(n&&this.tabs.each(function(s,a){return t(a).attr("aria-controls")===n?(i=s,!1):e}),null===i&&(i=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===i||-1===i)&&(i=this.tabs.length?0:!1)),i!==!1&&(i=this.tabs.index(this.tabs.eq(i)),-1===i&&(i=s?!1:0)),!s&&i===!1&&this.anchors.length&&(i=0),i},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(i){var s=t(this.document[0].activeElement).closest("li"),n=this.tabs.index(s),a=!0;if(!this._handlePageNav(i)){switch(i.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:n++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:a=!1,n--;break;case t.ui.keyCode.END:n=this.anchors.length-1;break;case t.ui.keyCode.HOME:n=0;break;case t.ui.keyCode.SPACE:return i.preventDefault(),clearTimeout(this.activating),this._activate(n),e;case t.ui.keyCode.ENTER:return i.preventDefault(),clearTimeout(this.activating),this._activate(n===this.options.active?!1:n),e;default:return}i.preventDefault(),clearTimeout(this.activating),n=this._focusNextTab(n,a),i.ctrlKey||(s.attr("aria-selected","false"),this.tabs.eq(n).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",n)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.focus())},_handlePageNav:function(i){return i.altKey&&i.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):i.altKey&&i.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):e},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).focus(),t},_setOption:function(t,i){return"active"===t?(this._activate(i),e):"disabled"===t?(this._setupDisabled(i),e):(this._super(t,i),"collapsible"===t&&(this.element.toggleClass("ui-tabs-collapsible",i),i||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(i),"heightStyle"===t&&this._setupHeightStyle(i),e)},_tabId:function(t){return t.attr("aria-controls")||"ui-tabs-"+i()},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=t(),this.anchors.each(function(i,n){var a,o,r,h=t(n).uniqueId().attr("id"),l=t(n).closest("li"),u=l.attr("aria-controls");s(n)?(a=n.hash,o=e.element.find(e._sanitizeSelector(a))):(r=e._tabId(l),a="#"+r,o=e.element.find(a),o.length||(o=e._createPanel(r),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),u&&l.data("ui-tabs-aria-controls",u),l.attr({"aria-controls":a.substring(1),"aria-labelledby":h}),o.attr("aria-labelledby",h)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.element.find("ol,ul").eq(0)},_createPanel:function(e){return t("<div>").attr("id",e).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(e){t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1);for(var i,s=0;i=this.tabs[s];s++)e===!0||-1!==t.inArray(s,e)?t(i).addClass("ui-state-disabled").attr("aria-disabled","true"):t(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=e},_setupEvents:function(e){var i={click:function(t){t.preventDefault()}};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?t():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):t(),u={oldTab:s,oldPanel:l,newTab:r?t():a,newPanel:h};e.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",e,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?t():a,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),e),this._toggle(e,u))},_toggle:function(e,i){function s(){a.running=!1,a._trigger("activate",e,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr({"aria-expanded":"false","aria-hidden":"true"}),i.oldTab.attr("aria-selected","false"),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr({"aria-expanded":"true","aria-hidden":"false"}),i.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(t){return"string"==typeof t&&(t=this.anchors.index(this.anchors.filter("[href$='"+t+"']"))),t},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(i){var s=this.options.disabled;s!==!1&&(i===e?s=!1:(i=this._getIndex(i),s=t.isArray(s)?t.map(s,function(t){return t!==i?t:null}):t.map(this.tabs,function(t,e){return e!==i?e:null})),this._setupDisabled(s))},disable:function(i){var s=this.options.disabled;if(s!==!0){if(i===e)s=!0;else{if(i=this._getIndex(i),-1!==t.inArray(i,s))return;s=t.isArray(s)?t.merge([i],s).sort():[i]}this._setupDisabled(s)}},load:function(e,i){e=this._getIndex(e);var n=this,a=this.tabs.eq(e),o=a.find(".ui-tabs-anchor"),r=this._getPanelForTab(a),h={tab:a,panel:r};s(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,h)),this.xhr&&"canceled"!==this.xhr.statusText&&(a.addClass("ui-tabs-loading"),r.attr("aria-busy","true"),this.xhr.success(function(t){setTimeout(function(){r.html(t),n._trigger("load",i,h)},1)}).complete(function(t,e){setTimeout(function(){"abort"===e&&n.panels.stop(!1,!0),a.removeClass("ui-tabs-loading"),r.removeAttr("aria-busy"),t===n.xhr&&delete n.xhr},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href"),beforeSend:function(e,a){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:a},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}})})(jQuery);(function(t){function e(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))}function i(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")}var s=0;t.widget("ui.tooltip",{version:"1.10.3",options:{content:function(){var e=t(this).attr("title")||"";return t("<a>").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(e,i){var s=this;return"disabled"===e?(this[i?"_disable":"_enable"](),this.options[e]=i,void 0):(this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e)}),void 0)},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.is("[title]")&&e.data("ui-tooltip-title",e.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))})},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,a=e?e.type:null;return"string"==typeof s?this._open(e,t,s):(i=s.call(t[0],function(i){t.data("ui-tooltip-open")&&n._delay(function(){e&&(e.type=a),this._open(e,t,i)})}),i&&this._open(e,t,i),void 0)},_open:function(i,s,n){function a(t){l.of=t,o.is(":hidden")||o.position(l)}var o,r,h,l=t.extend({},this.options.position);if(n){if(o=this._find(s),o.length)return o.find(".ui-tooltip-content").html(n),void 0;s.is("[title]")&&(i&&"mouseover"===i.type?s.attr("title",""):s.removeAttr("title")),o=this._tooltip(s),e(s,o.attr("id")),o.find(".ui-tooltip-content").html(n),this.options.track&&i&&/^mouse/.test(i.type)?(this._on(this.document,{mousemove:a}),a(i)):o.position(t.extend({of:s},this.options.position)),o.hide(),this._show(o,this.options.show),this.options.show&&this.options.show.delay&&(h=this.delayedShow=setInterval(function(){o.is(":visible")&&(a(l.of),clearInterval(h))},t.fx.interval)),this._trigger("open",i,{tooltip:o}),r={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var i=t.Event(e);i.currentTarget=s[0],this.close(i,!0)}},remove:function(){this._removeTooltip(o)}},i&&"mouseover"!==i.type||(r.mouseleave="close"),i&&"focusin"!==i.type||(r.focusout="close"),this._on(!0,s,r)}},close:function(e){var s=this,n=t(e?e.currentTarget:this.element),a=this._find(n);this.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&n.attr("title",n.data("ui-tooltip-title")),i(n),a.stop(!0),this._hide(a,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),this.closing=!0,this._trigger("close",e,{tooltip:a}),this.closing=!1)},_tooltip:function(e){var i="ui-tooltip-"+s++,n=t("<div>").attr({id:i,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return t("<div>").addClass("ui-tooltip-content").appendTo(n),n.appendTo(this.document[0].body),this.tooltips[i]=e,n},_find:function(e){var i=e.data("ui-tooltip-id");return i?t("#"+i):t()},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0),t("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))})}})})(jQuery);(function(t,e){var i="ui-effects-";t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(t,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[c[l].cache]=o[c[l].cache],n=s._rgba=o._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,a.transparent),s):a[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],l=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=l.support={},p=t("<p>")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),l.fn=t.extend(l.prototype,{parse:function(n,o,r,h){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(o),o=e);var u=this,d=t.type(n),p=this._rgba=[];return o!==e&&(n=[n,o,r,h],d="array"),"string"===d?this.parse(s(n)||a._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof l?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var a=s.cache;f(s.props,function(t,e){if(!u[a]&&s.to){if("alpha"===t||null==n[t])return;u[a]=s.to(u._rgba)}u[a][e.idx]=i(n[t],e,!0)}),u[a]&&0>t.inArray(null,u[a].slice(0,3))&&(u[a][3]=1,s.from&&(u._rgba=s.from(u[a])))}),this):e},is:function(t){var i=l(t),s=!0,n=this;return f(c,function(t,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=l(t),n=s._space(),a=c[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(t,n){var a=n.idx,o=r[a],l=s[a],c=u[n.type]||{};null!==l&&(null===o?h[a]=l:(c.mod&&(l-o>c.mod/2?o+=c.mod:o-l>c.mod/2&&(o-=c.mod)),h[a]=i((l-o)*e+o,n)))}),this[n](h)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(e)._rgba;return l(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,a=t[2]/255,o=t[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,c=r+h,u=.5*c;return e=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=u?l/c:l/(2-c),[Math.round(e)%360,i,u,null==o?1:o]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],a=t[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,e+1/3)),Math.round(255*n(r,o,e)),Math.round(255*n(r,o,e-1/3)),a]},f(c,function(s,n){var a=n.props,o=n.cache,h=n.to,c=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===e)return this[o].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[o].slice();return f(a,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=l(c(d)),n[o]=d,n):l(d)},f(a,function(e,i){l.fn[e]||(l.fn[e]=function(n){var a,o=t.type(n),h="alpha"===e?this._hsla?"hsla":"rgba":s,l=this[h](),c=l[i.idx];return"undefined"===o?c:("function"===o&&(n=n.call(this,c),o=t.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=c+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var a,o,r="";if("transparent"!==n&&("string"!==t.type(n)||(a=s(n)))){if(n=l(a||n),!d.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&o&&o.style;)try{r=t.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(h){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=l(e.elem,i),e.end=l(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},l.hook(o),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},a=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function i(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function s(e,i){var s,n,o={};for(s in i)n=i[s],e[s]!==n&&(a[s]||(t.fx.step[s]||!isNaN(parseFloat(n)))&&(o[s]=n));return o}var n=["add","remove","toggle"],a={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(jQuery.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(e,a,o,r){var h=t.speed(a,o,r);return this.queue(function(){var a,o=t(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var e=t(this);return{el:e,start:i(this)}}),a=function(){t.each(n,function(t,i){e[i]&&o[i+"Class"](e[i])})},a(),l=l.map(function(){return this.end=i(this.el[0]),this.diff=s(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var e=this,i=t.Deferred(),s=t.extend({},h,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,l.get()).done(function(){a(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),h.complete.call(o[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,a){return s?t.effects.animateClass.call(this,{add:i},s,n,a):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,a){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,a):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(i){return function(s,n,a,o,r){return"boolean"==typeof n||n===e?a?t.effects.animateClass.call(this,n?{add:s}:{remove:s},a,o,r):i.apply(this,arguments):t.effects.animateClass.call(this,{toggle:s},n,a,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,a){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,a)}})}(),function(){function s(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function n(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}t.extend(t.effects,{version:"1.10.3",save:function(t,e){for(var s=0;e.length>s;s++)null!==e[s]&&t.data(i+e[s],t[0].style[e[s]])},restore:function(t,s){var n,a;for(a=0;s.length>a;a++)null!==s[a]&&(n=t.data(i+s[a]),n===e&&(n=""),t.css(s[a],n))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return e.wrap(s),(e[0]===a||t.contains(e[0],a))&&t(a).focus(),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).focus()),e},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var a=e.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),t.fn.extend({effect:function(){function e(e){function s(){t.isFunction(a)&&a.call(n[0]),t.isFunction(e)&&e()}var n=t(this),a=i.complete,r=i.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),s()):o.call(n[0],i,s)}var i=s.apply(this,arguments),n=i.mode,a=i.queue,o=t.effects.effect[i.effect];return t.fx.off||!o?n?this[n](i.duration,i.complete):this.each(function(){i.complete&&i.complete.call(this)}):a===!1?this.each(e):this.queue(a||"fx",e)},show:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="show",this.effect.call(this,i)}}(t.fn.show),hide:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="hide",this.effect.call(this,i)}}(t.fn.hide),toggle:function(t){return function(e){if(n(e)||"boolean"==typeof e)return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="toggle",this.effect.call(this,i)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s}})}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}()})(jQuery);(function(t){var e=/up|down|vertical/,i=/up|left|vertical|horizontal/;t.effects.effect.blind=function(s,n){var a,o,r,h=t(this),l=["position","top","bottom","left","right","height","width"],c=t.effects.setMode(h,s.mode||"hide"),u=s.direction||"up",d=e.test(u),p=d?"height":"width",f=d?"top":"left",m=i.test(u),g={},v="show"===c;h.parent().is(".ui-effects-wrapper")?t.effects.save(h.parent(),l):t.effects.save(h,l),h.show(),a=t.effects.createWrapper(h).css({overflow:"hidden"}),o=a[p](),r=parseFloat(a.css(f))||0,g[p]=v?o:0,m||(h.css(d?"bottom":"right",0).css(d?"top":"left","auto").css({position:"absolute"}),g[f]=v?r:o+r),v&&(a.css(p,0),m||a.css(f,r+o)),a.animate(g,{duration:s.duration,easing:s.easing,queue:!1,complete:function(){"hide"===c&&h.hide(),t.effects.restore(h,l),t.effects.removeWrapper(h),n()}})}})(jQuery);(function(t){t.effects.effect.bounce=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],h=t.effects.setMode(o,e.mode||"effect"),l="hide"===h,c="show"===h,u=e.direction||"up",d=e.distance,p=e.times||5,f=2*p+(c||l?1:0),m=e.duration/f,g=e.easing,v="up"===u||"down"===u?"top":"left",_="up"===u||"left"===u,b=o.queue(),y=b.length;for((c||l)&&r.push("opacity"),t.effects.save(o,r),o.show(),t.effects.createWrapper(o),d||(d=o["top"===v?"outerHeight":"outerWidth"]()/3),c&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,_?2*-d:2*d).animate(a,m,g)),l&&(d/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(_?"-=":"+=")+d,o.animate(n,m,g).animate(a,m,g),d=l?2*d:d/2;l&&(n={opacity:0},n[v]=(_?"-=":"+=")+d,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}),y>1&&b.splice.apply(b,[1,0].concat(b.splice(y,f+1))),o.dequeue()}})(jQuery);(function(t){t.effects.effect.clip=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],h=t.effects.setMode(o,e.mode||"hide"),l="show"===h,c=e.direction||"vertical",u="vertical"===c,d=u?"height":"width",p=u?"top":"left",f={};t.effects.save(o,r),o.show(),s=t.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[d](),l&&(n.css(d,0),n.css(p,a/2)),f[d]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){l||o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.drop=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","opacity","height","width"],o=t.effects.setMode(n,e.mode||"hide"),r="show"===o,h=e.direction||"left",l="up"===h||"down"===h?"top":"left",c="up"===h||"left"===h?"pos":"neg",u={opacity:r?1:0};t.effects.save(n,a),n.show(),t.effects.createWrapper(n),s=e.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===c?-s:s),u[l]=(r?"pos"===c?"+=":"-=":"pos"===c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.explode=function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),m||p.hide(),i()}var a,o,r,h,l,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=t.effects.setMode(p,e.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/d),_=Math.ceil(p.outerHeight()/u),b=[];for(a=0;u>a;a++)for(h=g.top+a*_,c=a-(u-1)/2,o=0;d>o;o++)r=g.left+o*v,l=o-(d-1)/2,p.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*_}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:_,left:r+(m?l*v:0),top:h+(m?c*_:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:c*_),opacity:m?1:0},e.duration||500,e.easing,s)}})(jQuery);(function(t){t.effects.effect.fade=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}})(jQuery);(function(t){t.effects.effect.fold=function(e,i){var s,n,a=t(this),o=["position","top","bottom","left","right","height","width"],r=t.effects.setMode(a,e.mode||"hide"),h="show"===r,l="hide"===r,c=e.size||15,u=/([0-9]+)%/.exec(c),d=!!e.horizFirst,p=h!==d,f=p?["width","height"]:["height","width"],m=e.duration/2,g={},v={};t.effects.save(a,o),a.show(),s=t.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],u&&(c=parseInt(u[1],10)/100*n[l?0:1]),h&&s.css(d?{height:0,width:c}:{height:c,width:0}),g[f[0]]=h?n[0]:c,v[f[1]]=h?n[1]:0,s.animate(g,m,e.easing).animate(v,m,e.easing,function(){l&&a.hide(),t.effects.restore(a,o),t.effects.removeWrapper(a),i()})}})(jQuery);(function(t){t.effects.effect.highlight=function(e,i){var s=t(this),n=["backgroundImage","backgroundColor","opacity"],a=t.effects.setMode(s,e.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),t.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(o,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===a&&s.hide(),t.effects.restore(s,n),i()}})}})(jQuery);(function(t){t.effects.effect.pulsate=function(e,i){var s,n=t(this),a=t.effects.setMode(n,e.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(e.times||5)+(h?1:0),c=e.duration/l,u=0,d=n.queue(),p=d.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),u=1),s=1;l>s;s++)n.animate({opacity:u},c,e.easing),u=1-u;n.animate({opacity:u},c,e.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&d.splice.apply(d,[1,0].concat(d.splice(p,l+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.puff=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"hide"),a="hide"===n,o=parseInt(e.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};t.extend(e,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(e)},t.effects.effect.scale=function(e,i){var s=t(this),n=t.extend(!0,{},e),a=t.effects.setMode(s,e.mode||"effect"),o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"hide"===a?0:100),r=e.direction||"both",h=e.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},c={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=e.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*c.y,width:l.width*c.x,outerHeight:l.outerHeight*c.y,outerWidth:l.outerWidth*c.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},t.effects.effect.size=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],c=["fontSize"],u=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],d=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=t.effects.setMode(o,e.mode||"effect"),f=e.restore||"effect"!==p,m=e.scale||"both",g=e.origin||["middle","center"],v=o.css("position"),_=f?r:h,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===e.mode&&"show"===p?(o.from=e.to||b,o.to=e.from||s):(o.from=e.from||("show"===p?b:s),o.to=e.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(_=_.concat(u),o.from=t.effects.setTransition(o,u,a.from.y,o.from),o.to=t.effects.setTransition(o,u,a.to.y,o.to)),a.from.x!==a.to.x&&(_=_.concat(d),o.from=t.effects.setTransition(o,d,a.from.x,o.from),o.to=t.effects.setTransition(o,d,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(_=_.concat(c).concat(l),o.from=t.effects.setTransition(o,c,a.from.y,o.from),o.to=t.effects.setTransition(o,c,a.to.y,o.to)),t.effects.save(o,_),o.show(),t.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=t.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(u=u.concat(["marginTop","marginBottom"]).concat(c),d=d.concat(["marginLeft","marginRight"]),l=r.concat(u).concat(d),o.find("*[width]").each(function(){var i=t(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&t.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=t.effects.setTransition(i,u,a.from.y,i.from),i.to=t.effects.setTransition(i,u,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=t.effects.setTransition(i,d,a.from.x,i.from),i.to=t.effects.setTransition(i,d,a.to.x,i.to)),i.css(i.from),i.animate(i.to,e.duration,e.easing,function(){f&&t.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),t.effects.restore(o,_),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):t.each(["top","left"],function(t,e){o.css(e,function(e,i){var s=parseInt(i,10),n=t?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.shake=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","height","width"],o=t.effects.setMode(n,e.mode||"effect"),r=e.direction||"left",h=e.distance||20,l=e.times||3,c=2*l+1,u=Math.round(e.duration/c),d="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),_=v.length;for(t.effects.save(n,a),n.show(),t.effects.createWrapper(n),f[d]=(p?"-=":"+=")+h,m[d]=(p?"+=":"-=")+2*h,g[d]=(p?"-=":"+=")+2*h,n.animate(f,u,e.easing),s=1;l>s;s++)n.animate(m,u,e.easing).animate(g,u,e.easing);n.animate(m,u,e.easing).animate(f,u/2,e.easing).queue(function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}),_>1&&v.splice.apply(v,[1,0].concat(v.splice(_,c+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.slide=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","width","height"],o=t.effects.setMode(n,e.mode||"show"),r="show"===o,h=e.direction||"left",l="up"===h||"down"===h?"top":"left",c="up"===h||"left"===h,u={};t.effects.save(n,a),n.show(),s=e.distance||n["top"===l?"outerHeight":"outerWidth"](!0),t.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,c?isNaN(s)?"-"+s:-s:s),u[l]=(r?c?"+=":"-=":c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.transfer=function(e,i){var s=t(this),n=t(e.to),a="fixed"===n.css("position"),o=t("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),c={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},u=s.offset(),d=t("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(e.className).css({top:u.top-r,left:u.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){d.remove(),i()})}})(jQuery); \ No newline at end of file diff --git a/web/extensions/jquery/js/jquery-ui-1.9.1.custom.min.js b/web/extensions/jquery/js/jquery-ui-1.9.1.custom.min.js new file mode 100644 index 0000000..080c235 --- /dev/null +++ b/web/extensions/jquery/js/jquery-ui-1.9.1.custom.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.9.1 - 2012-11-16 +* http://jqueryui.com +* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js +* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */ + +(function(e,t){function i(t,n){var r,i,o,u=t.nodeName.toLowerCase();return"area"===u?(r=t.parentNode,i=r.name,!t.href||!i||r.nodeName.toLowerCase()!=="map"?!1:(o=e("img[usemap=#"+i+"]")[0],!!o&&s(o))):(/input|select|textarea|button|object/.test(u)?!t.disabled:"a"===u?t.href||n:n)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().andSelf().filter(function(){return e.css(this,"visibility")==="hidden"}).length}var n=0,r=/^ui-id-\d+$/;e.ui=e.ui||{};if(e.ui.version)return;e.extend(e.ui,{version:"1.9.1",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({_focus:e.fn.focus,focus:function(t,n){return typeof t=="number"?this.each(function(){var r=this;setTimeout(function(){e(r).focus(),n&&n.call(r)},t)}):this._focus.apply(this,arguments)},scrollParent:function(){var t;return e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?t=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):t=this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(n){if(n!==t)return this.css("zIndex",n);if(this.length){var r=e(this[0]),i,s;while(r.length&&r[0]!==document){i=r.css("position");if(i==="absolute"||i==="relative"||i==="fixed"){s=parseInt(r.css("zIndex"),10);if(!isNaN(s)&&s!==0)return s}r=r.parent()}}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++n)})},removeUniqueId:function(){return this.each(function(){r.test(this.id)&&e(this).removeAttr("id")})}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(n,r){function u(t,n,r,s){return e.each(i,function(){n-=parseFloat(e.css(t,"padding"+this))||0,r&&(n-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(n-=parseFloat(e.css(t,"margin"+this))||0)}),n}var i=r==="Width"?["Left","Right"]:["Top","Bottom"],s=r.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+r]=function(n){return n===t?o["inner"+r].call(this):this.each(function(){e(this).css(s,u(this,n)+"px")})},e.fn["outer"+r]=function(t,n){return typeof t!="number"?o["outer"+r].call(this,t):this.each(function(){e(this).css(s,u(this,t,!0,n)+"px")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(n){return!!e.data(n,t)}}):function(t,n,r){return!!e.data(t,r[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var n=e.attr(t,"tabindex"),r=isNaN(n);return(r||n>=0)&&i(t,!r)}}),e(function(){var t=document.body,n=t.appendChild(n=document.createElement("div"));n.offsetHeight,e.extend(n.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),e.support.minHeight=n.offsetHeight===100,e.support.selectstart="onselectstart"in n,t.removeChild(n).style.display="none"}),function(){var t=/msie ([\w.]+)/.exec(navigator.userAgent.toLowerCase())||[];e.ui.ie=t.length?!0:!1,e.ui.ie6=parseFloat(t[1],10)===6}(),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,n,r){var i,s=e.ui[t].prototype;for(i in r)s.plugins[i]=s.plugins[i]||[],s.plugins[i].push([n,r[i]])},call:function(e,t,n){var r,i=e.plugins[t];if(!i||!e.element[0].parentNode||e.element[0].parentNode.nodeType===11)return;for(r=0;r<i.length;r++)e.options[i[r][0]]&&i[r][1].apply(e.element,n)}},contains:e.contains,hasScroll:function(t,n){if(e(t).css("overflow")==="hidden")return!1;var r=n&&n==="left"?"scrollLeft":"scrollTop",i=!1;return t[r]>0?!0:(t[r]=1,i=t[r]>0,t[r]=0,i)},isOverAxis:function(e,t,n){return e>t&&e<t+n},isOver:function(t,n,r,i,s,o){return e.ui.isOverAxis(t,r,s)&&e.ui.isOverAxis(n,i,o)}})})(jQuery);(function(e,t){var n=0,r=Array.prototype.slice,i=e.cleanData;e.cleanData=function(t){for(var n=0,r;(r=t[n])!=null;n++)try{e(r).triggerHandler("remove")}catch(s){}i(t)},e.widget=function(t,n,r){var i,s,o,u,a=t.split(".")[0];t=t.split(".")[1],i=a+"-"+t,r||(r=n,n=e.Widget),e.expr[":"][i.toLowerCase()]=function(t){return!!e.data(t,i)},e[a]=e[a]||{},s=e[a][t],o=e[a][t]=function(e,t){if(!this._createWidget)return new o(e,t);arguments.length&&this._createWidget(e,t)},e.extend(o,s,{version:r.version,_proto:e.extend({},r),_childConstructors:[]}),u=new n,u.options=e.widget.extend({},u.options),e.each(r,function(t,i){e.isFunction(i)&&(r[t]=function(){var e=function(){return n.prototype[t].apply(this,arguments)},r=function(e){return n.prototype[t].apply(this,e)};return function(){var t=this._super,n=this._superApply,s;return this._super=e,this._superApply=r,s=i.apply(this,arguments),this._super=t,this._superApply=n,s}}())}),o.prototype=e.widget.extend(u,{widgetEventPrefix:u.widgetEventPrefix||t},r,{constructor:o,namespace:a,widgetName:t,widgetBaseClass:i,widgetFullName:i}),s?(e.each(s._childConstructors,function(t,n){var r=n.prototype;e.widget(r.namespace+"."+r.widgetName,o,n._proto)}),delete s._childConstructors):n._childConstructors.push(o),e.widget.bridge(t,o)},e.widget.extend=function(n){var i=r.call(arguments,1),s=0,o=i.length,u,a;for(;s<o;s++)for(u in i[s])a=i[s][u],i[s].hasOwnProperty(u)&&a!==t&&(e.isPlainObject(a)?n[u]=e.isPlainObject(n[u])?e.widget.extend({},n[u],a):e.widget.extend({},a):n[u]=a);return n},e.widget.bridge=function(n,i){var s=i.prototype.widgetFullName;e.fn[n]=function(o){var u=typeof o=="string",a=r.call(arguments,1),f=this;return o=!u&&a.length?e.widget.extend.apply(null,[o].concat(a)):o,u?this.each(function(){var r,i=e.data(this,s);if(!i)return e.error("cannot call methods on "+n+" prior to initialization; "+"attempted to call method '"+o+"'");if(!e.isFunction(i[o])||o.charAt(0)==="_")return e.error("no such method '"+o+"' for "+n+" widget instance");r=i[o].apply(i,a);if(r!==i&&r!==t)return f=r&&r.jquery?f.pushStack(r.get()):r,!1}):this.each(function(){var t=e.data(this,s);t?t.option(o||{})._init():new i(o,this)}),f}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,r){r=e(r||this.defaultElement||this)[0],this.element=e(r),this.uuid=n++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),r!==this&&(e.data(r,this.widgetName,this),e.data(r,this.widgetFullName,this),this._on(this.element,{remove:function(e){e.target===r&&this.destroy()}}),this.document=e(r.style?r.ownerDocument:r.document||r),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(n,r){var i=n,s,o,u;if(arguments.length===0)return e.widget.extend({},this.options);if(typeof n=="string"){i={},s=n.split("."),n=s.shift();if(s.length){o=i[n]=e.widget.extend({},this.options[n]);for(u=0;u<s.length-1;u++)o[s[u]]=o[s[u]]||{},o=o[s[u]];n=s.pop();if(r===t)return o[n]===t?null:o[n];o[n]=r}else{if(r===t)return this.options[n]===t?null:this.options[n];i[n]=r}}return this._setOptions(i),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,e==="disabled"&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(t,n){var r,i=this;n?(t=r=e(t),this.bindings=this.bindings.add(t)):(n=t,t=this.element,r=this.widget()),e.each(n,function(n,s){function o(){if(i.options.disabled===!0||e(this).hasClass("ui-state-disabled"))return;return(typeof s=="string"?i[s]:s).apply(i,arguments)}typeof s!="string"&&(o.guid=s.guid=s.guid||o.guid||e.guid++);var u=n.match(/^(\w+)\s*(.*)$/),a=u[1]+i.eventNamespace,f=u[2];f?r.delegate(f,a,o):t.bind(a,o)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function n(){return(typeof e=="string"?r[e]:e).apply(r,arguments)}var r=this;return setTimeout(n,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,n,r){var i,s,o=this.options[t];r=r||{},n=e.Event(n),n.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),n.target=this.element[0],s=n.originalEvent;if(s)for(i in s)i in n||(n[i]=s[i]);return this.element.trigger(n,r),!(e.isFunction(o)&&o.apply(this.element[0],[n].concat(r))===!1||n.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,n){e.Widget.prototype["_"+t]=function(r,i,s){typeof i=="string"&&(i={effect:i});var o,u=i?i===!0||typeof i=="number"?n:i.effect||n:t;i=i||{},typeof i=="number"&&(i={duration:i}),o=!e.isEmptyObject(i),i.complete=s,i.delay&&r.delay(i.delay),o&&e.effects&&(e.effects.effect[u]||e.uiBackCompat!==!1&&e.effects[u])?r[t](i):u!==t&&r[u]?r[u](i.duration,i.easing,s):r.queue(function(n){e(this)[t](),s&&s.call(r[0]),n()})}}),e.uiBackCompat!==!1&&(e.Widget.prototype._getCreateOptions=function(){return e.metadata&&e.metadata.get(this.element[0])[this.widgetName]})})(jQuery);(function(e,t){var n=!1;e(document).mouseup(function(e){n=!1}),e.widget("ui.mouse",{version:"1.9.1",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(n){if(!0===e.data(n.target,t.widgetName+".preventClickEvent"))return e.removeData(n.target,t.widgetName+".preventClickEvent"),n.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(n)return;this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var r=this,i=t.which===1,s=typeof this.options.cancel=="string"&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;if(!i||s||!this._mouseCapture(t))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){r.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)){this._mouseStarted=this._mouseStart(t)!==!1;if(!this._mouseStarted)return t.preventDefault(),!0}return!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return r._mouseMove(e)},this._mouseUpDelegate=function(e){return r._mouseUp(e)},e(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),n=!0,!0},_mouseMove:function(t){return!e.ui.ie||document.documentMode>=9||!!t.button?this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted):this._mouseUp(t)},_mouseUp:function(t){return e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(e){return this.mouseDelayMet},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return!0}})})(jQuery);(function(e,t){function h(e,t,n){return[parseInt(e[0],10)*(l.test(e[0])?t/100:1),parseInt(e[1],10)*(l.test(e[1])?n/100:1)]}function p(t,n){return parseInt(e.css(t,n),10)||0}e.ui=e.ui||{};var n,r=Math.max,i=Math.abs,s=Math.round,o=/left|center|right/,u=/top|center|bottom/,a=/[\+\-]\d+%?/,f=/^\w+/,l=/%$/,c=e.fn.position;e.position={scrollbarWidth:function(){if(n!==t)return n;var r,i,s=e("<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),o=s.children()[0];return e("body").append(s),r=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,r===i&&(i=s[0].clientWidth),s.remove(),n=r-i},getScrollInfo:function(t){var n=t.isWindow?"":t.element.css("overflow-x"),r=t.isWindow?"":t.element.css("overflow-y"),i=n==="scroll"||n==="auto"&&t.width<t.element[0].scrollWidth,s=r==="scroll"||r==="auto"&&t.height<t.element[0].scrollHeight;return{width:i?e.position.scrollbarWidth():0,height:s?e.position.scrollbarWidth():0}},getWithinInfo:function(t){var n=e(t||window),r=e.isWindow(n[0]);return{element:n,isWindow:r,offset:n.offset()||{left:0,top:0},scrollLeft:n.scrollLeft(),scrollTop:n.scrollTop(),width:r?n.width():n.outerWidth(),height:r?n.height():n.outerHeight()}}},e.fn.position=function(t){if(!t||!t.of)return c.apply(this,arguments);t=e.extend({},t);var n,l,d,v,m,g=e(t.of),y=e.position.getWithinInfo(t.within),b=e.position.getScrollInfo(y),w=g[0],E=(t.collision||"flip").split(" "),S={};return w.nodeType===9?(l=g.width(),d=g.height(),v={top:0,left:0}):e.isWindow(w)?(l=g.width(),d=g.height(),v={top:g.scrollTop(),left:g.scrollLeft()}):w.preventDefault?(t.at="left top",l=d=0,v={top:w.pageY,left:w.pageX}):(l=g.outerWidth(),d=g.outerHeight(),v=g.offset()),m=e.extend({},v),e.each(["my","at"],function(){var e=(t[this]||"").split(" "),n,r;e.length===1&&(e=o.test(e[0])?e.concat(["center"]):u.test(e[0])?["center"].concat(e):["center","center"]),e[0]=o.test(e[0])?e[0]:"center",e[1]=u.test(e[1])?e[1]:"center",n=a.exec(e[0]),r=a.exec(e[1]),S[this]=[n?n[0]:0,r?r[0]:0],t[this]=[f.exec(e[0])[0],f.exec(e[1])[0]]}),E.length===1&&(E[1]=E[0]),t.at[0]==="right"?m.left+=l:t.at[0]==="center"&&(m.left+=l/2),t.at[1]==="bottom"?m.top+=d:t.at[1]==="center"&&(m.top+=d/2),n=h(S.at,l,d),m.left+=n[0],m.top+=n[1],this.each(function(){var o,u,a=e(this),f=a.outerWidth(),c=a.outerHeight(),w=p(this,"marginLeft"),x=p(this,"marginTop"),T=f+w+p(this,"marginRight")+b.width,N=c+x+p(this,"marginBottom")+b.height,C=e.extend({},m),k=h(S.my,a.outerWidth(),a.outerHeight());t.my[0]==="right"?C.left-=f:t.my[0]==="center"&&(C.left-=f/2),t.my[1]==="bottom"?C.top-=c:t.my[1]==="center"&&(C.top-=c/2),C.left+=k[0],C.top+=k[1],e.support.offsetFractions||(C.left=s(C.left),C.top=s(C.top)),o={marginLeft:w,marginTop:x},e.each(["left","top"],function(r,i){e.ui.position[E[r]]&&e.ui.position[E[r]][i](C,{targetWidth:l,targetHeight:d,elemWidth:f,elemHeight:c,collisionPosition:o,collisionWidth:T,collisionHeight:N,offset:[n[0]+k[0],n[1]+k[1]],my:t.my,at:t.at,within:y,elem:a})}),e.fn.bgiframe&&a.bgiframe(),t.using&&(u=function(e){var n=v.left-C.left,s=n+l-f,o=v.top-C.top,u=o+d-c,h={target:{element:g,left:v.left,top:v.top,width:l,height:d},element:{element:a,left:C.left,top:C.top,width:f,height:c},horizontal:s<0?"left":n>0?"right":"center",vertical:u<0?"top":o>0?"bottom":"middle"};l<f&&i(n+s)<l&&(h.horizontal="center"),d<c&&i(o+u)<d&&(h.vertical="middle"),r(i(n),i(s))>r(i(o),i(u))?h.important="horizontal":h.important="vertical",t.using.call(this,e,h)}),a.offset(e.extend(C,{using:u}))})},e.ui.position={fit:{left:function(e,t){var n=t.within,i=n.isWindow?n.scrollLeft:n.offset.left,s=n.width,o=e.left-t.collisionPosition.marginLeft,u=i-o,a=o+t.collisionWidth-s-i,f;t.collisionWidth>s?u>0&&a<=0?(f=e.left+u+t.collisionWidth-s-i,e.left+=u-f):a>0&&u<=0?e.left=i:u>a?e.left=i+s-t.collisionWidth:e.left=i:u>0?e.left+=u:a>0?e.left-=a:e.left=r(e.left-o,e.left)},top:function(e,t){var n=t.within,i=n.isWindow?n.scrollTop:n.offset.top,s=t.within.height,o=e.top-t.collisionPosition.marginTop,u=i-o,a=o+t.collisionHeight-s-i,f;t.collisionHeight>s?u>0&&a<=0?(f=e.top+u+t.collisionHeight-s-i,e.top+=u-f):a>0&&u<=0?e.top=i:u>a?e.top=i+s-t.collisionHeight:e.top=i:u>0?e.top+=u:a>0?e.top-=a:e.top=r(e.top-o,e.top)}},flip:{left:function(e,t){var n=t.within,r=n.offset.left+n.scrollLeft,s=n.width,o=n.isWindow?n.scrollLeft:n.offset.left,u=e.left-t.collisionPosition.marginLeft,a=u-o,f=u+t.collisionWidth-s-o,l=t.my[0]==="left"?-t.elemWidth:t.my[0]==="right"?t.elemWidth:0,c=t.at[0]==="left"?t.targetWidth:t.at[0]==="right"?-t.targetWidth:0,h=-2*t.offset[0],p,d;if(a<0){p=e.left+l+c+h+t.collisionWidth-s-r;if(p<0||p<i(a))e.left+=l+c+h}else if(f>0){d=e.left-t.collisionPosition.marginLeft+l+c+h-o;if(d>0||i(d)<f)e.left+=l+c+h}},top:function(e,t){var n=t.within,r=n.offset.top+n.scrollTop,s=n.height,o=n.isWindow?n.scrollTop:n.offset.top,u=e.top-t.collisionPosition.marginTop,a=u-o,f=u+t.collisionHeight-s-o,l=t.my[1]==="top",c=l?-t.elemHeight:t.my[1]==="bottom"?t.elemHeight:0,h=t.at[1]==="top"?t.targetHeight:t.at[1]==="bottom"?-t.targetHeight:0,p=-2*t.offset[1],d,v;a<0?(v=e.top+c+h+p+t.collisionHeight-s-r,e.top+c+h+p>a&&(v<0||v<i(a))&&(e.top+=c+h+p)):f>0&&(d=e.top-t.collisionPosition.marginTop+c+h+p-o,e.top+c+h+p>f&&(d>0||i(d)<f)&&(e.top+=c+h+p))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,n,r,i,s,o=document.getElementsByTagName("body")[0],u=document.createElement("div");t=document.createElement(o?"div":"body"),r={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&e.extend(r,{position:"absolute",left:"-1000px",top:"-1000px"});for(s in r)t.style[s]=r[s];t.appendChild(u),n=o||document.documentElement,n.insertBefore(t,n.firstChild),u.style.cssText="position: absolute; left: 10.7432222px;",i=e(u).offset().left,e.support.offsetFractions=i>10&&i<11,t.innerHTML="",n.removeChild(t)}(),e.uiBackCompat!==!1&&function(e){var n=e.fn.position;e.fn.position=function(r){if(!r||!r.offset)return n.call(this,r);var i=r.offset.split(" "),s=r.at.split(" ");return i.length===1&&(i[1]=i[0]),/^\d/.test(i[0])&&(i[0]="+"+i[0]),/^\d/.test(i[1])&&(i[1]="+"+i[1]),s.length===1&&(/left|center|right/.test(s[0])?s[1]="center":(s[1]=s[0],s[0]="center")),n.call(this,e.extend(r,{at:s[0]+i[0]+" "+s[1]+i[1],offset:t}))}}(jQuery)})(jQuery);(function(e,t){e.widget("ui.draggable",e.ui.mouse,{version:"1.9.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(t){var n=this.options;return this.helper||n.disabled||e(t.target).is(".ui-resizable-handle")?!1:(this.handle=this._getHandle(t),this.handle?(e(n.iframeFix===!0?"iframe":n.iframeFix).each(function(){e('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var n=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,n.cursorAt&&this._adjustOffsetFromHelper(n.cursorAt),n.containment&&this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,n){this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute");if(!n){var r=this._uiHash();if(this._trigger("drag",t,r)===!1)return this._mouseUp({}),!1;this.position=r.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";return e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var n=!1;e.ui.ddmanager&&!this.options.dropBehaviour&&(n=e.ui.ddmanager.drop(this,t)),this.dropped&&(n=this.dropped,this.dropped=!1);var r=this.element[0],i=!1;while(r&&(r=r.parentNode))r==document&&(i=!0);if(!i&&this.options.helper==="original")return!1;if(this.options.revert=="invalid"&&!n||this.options.revert=="valid"&&n||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,n)){var s=this;e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){s._trigger("stop",t)!==!1&&s._clear()})}else this._trigger("stop",t)!==!1&&this._clear();return!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){var n=!this.options.handle||!e(this.options.handle,this.element).length?!0:!1;return e(this.options.handle,this.element).find("*").andSelf().each(function(){this==t.target&&(n=!0)}),n},_createHelper:function(t){var n=this.options,r=e.isFunction(n.helper)?e(n.helper.apply(this.element[0],[t])):n.helper=="clone"?this.element.clone().removeAttr("id"):this.element;return r.parents("body").length||r.appendTo(n.appendTo=="parent"?this.element[0].parentNode:n.appendTo),r[0]!=this.element[0]&&!/(fixed|absolute)/.test(r.css("position"))&&r.css("position","absolute"),r},_adjustOffsetFromHelper:function(t){typeof t=="string"&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&e.ui.ie)t={top:0,left:0};return{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var e=this.element.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t=this.options;t.containment=="parent"&&(t.containment=this.helper[0].parentNode);if(t.containment=="document"||t.containment=="window")this.containment=[t.containment=="document"?0:e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t.containment=="document"?0:e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(t.containment=="document"?0:e(window).scrollLeft())+e(t.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(t.containment=="document"?0:e(window).scrollTop())+(e(t.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(t.containment)&&t.containment.constructor!=Array){var n=e(t.containment),r=n[0];if(!r)return;var i=n.offset(),s=e(r).css("overflow")!="hidden";this.containment=[(parseInt(e(r).css("borderLeftWidth"),10)||0)+(parseInt(e(r).css("paddingLeft"),10)||0),(parseInt(e(r).css("borderTopWidth"),10)||0)+(parseInt(e(r).css("paddingTop"),10)||0),(s?Math.max(r.scrollWidth,r.offsetWidth):r.offsetWidth)-(parseInt(e(r).css("borderLeftWidth"),10)||0)-(parseInt(e(r).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(s?Math.max(r.scrollHeight,r.offsetHeight):r.offsetHeight)-(parseInt(e(r).css("borderTopWidth"),10)||0)-(parseInt(e(r).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=n}else t.containment.constructor==Array&&(this.containment=t.containment)},_convertPositionTo:function(t,n){n||(n=this.position);var r=t=="absolute"?1:-1,i=this.options,s=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(s[0].tagName);return{top:n.top+this.offset.relative.top*r+this.offset.parent.top*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():o?0:s.scrollTop())*r,left:n.left+this.offset.relative.left*r+this.offset.parent.left*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():o?0:s.scrollLeft())*r}},_generatePosition:function(t){var n=this.options,r=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,i=/(html|body)/i.test(r[0].tagName),s=t.pageX,o=t.pageY;if(this.originalPosition){var u;if(this.containment){if(this.relative_container){var a=this.relative_container.offset();u=[this.containment[0]+a.left,this.containment[1]+a.top,this.containment[2]+a.left,this.containment[3]+a.top]}else u=this.containment;t.pageX-this.offset.click.left<u[0]&&(s=u[0]+this.offset.click.left),t.pageY-this.offset.click.top<u[1]&&(o=u[1]+this.offset.click.top),t.pageX-this.offset.click.left>u[2]&&(s=u[2]+this.offset.click.left),t.pageY-this.offset.click.top>u[3]&&(o=u[3]+this.offset.click.top)}if(n.grid){var f=n.grid[1]?this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1]:this.originalPageY;o=u?f-this.offset.click.top<u[1]||f-this.offset.click.top>u[3]?f-this.offset.click.top<u[1]?f+n.grid[1]:f-n.grid[1]:f:f;var l=n.grid[0]?this.originalPageX+Math.round((s-this.originalPageX)/n.grid[0])*n.grid[0]:this.originalPageX;s=u?l-this.offset.click.left<u[0]||l-this.offset.click.left>u[2]?l-this.offset.click.left<u[0]?l+n.grid[0]:l-n.grid[0]:l:l}}return{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():i?0:r.scrollTop()),left:s-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():i?0:r.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(t,n,r){return r=r||this._uiHash(),e.ui.plugin.call(this,t,[n,r]),t=="drag"&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,n,r)},plugins:{},_uiHash:function(e){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,n){var r=e(this).data("draggable"),i=r.options,s=e.extend({},n,{item:r.element});r.sortables=[],e(i.connectToSortable).each(function(){var n=e.data(this,"sortable");n&&!n.options.disabled&&(r.sortables.push({instance:n,shouldRevert:n.options.revert}),n.refreshPositions(),n._trigger("activate",t,s))})},stop:function(t,n){var r=e(this).data("draggable"),i=e.extend({},n,{item:r.element});e.each(r.sortables,function(){this.instance.isOver?(this.instance.isOver=0,r.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,r.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,i))})},drag:function(t,n){var r=e(this).data("draggable"),i=this,s=function(t){var n=this.offset.click.top,r=this.offset.click.left,i=this.positionAbs.top,s=this.positionAbs.left,o=t.height,u=t.width,a=t.top,f=t.left;return e.ui.isOver(i+n,s+r,a,f,o,u)};e.each(r.sortables,function(s){var o=!1,u=this;this.instance.positionAbs=r.positionAbs,this.instance.helperProportions=r.helperProportions,this.instance.offset.click=r.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(o=!0,e.each(r.sortables,function(){return this.instance.positionAbs=r.positionAbs,this.instance.helperProportions=r.helperProportions,this.instance.offset.click=r.offset.click,this!=u&&this.instance._intersectsWith(this.instance.containerCache)&&e.ui.contains(u.instance.element[0],this.instance.element[0])&&(o=!1),o})),o?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(i).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return n.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=r.offset.click.top,this.instance.offset.click.left=r.offset.click.left,this.instance.offset.parent.left-=r.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=r.offset.parent.top-this.instance.offset.parent.top,r._trigger("toSortable",t),r.dropped=this.instance.element,r.currentItem=r.element,this.instance.fromOutside=r),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),r._trigger("fromSortable",t),r.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,n){var r=e("body"),i=e(this).data("draggable").options;r.css("cursor")&&(i._cursor=r.css("cursor")),r.css("cursor",i.cursor)},stop:function(t,n){var r=e(this).data("draggable").options;r._cursor&&e("body").css("cursor",r._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,n){var r=e(n.helper),i=e(this).data("draggable").options;r.css("opacity")&&(i._opacity=r.css("opacity")),r.css("opacity",i.opacity)},stop:function(t,n){var r=e(this).data("draggable").options;r._opacity&&e(n.helper).css("opacity",r._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(t,n){var r=e(this).data("draggable");r.scrollParent[0]!=document&&r.scrollParent[0].tagName!="HTML"&&(r.overflowOffset=r.scrollParent.offset())},drag:function(t,n){var r=e(this).data("draggable"),i=r.options,s=!1;if(r.scrollParent[0]!=document&&r.scrollParent[0].tagName!="HTML"){if(!i.axis||i.axis!="x")r.overflowOffset.top+r.scrollParent[0].offsetHeight-t.pageY<i.scrollSensitivity?r.scrollParent[0].scrollTop=s=r.scrollParent[0].scrollTop+i.scrollSpeed:t.pageY-r.overflowOffset.top<i.scrollSensitivity&&(r.scrollParent[0].scrollTop=s=r.scrollParent[0].scrollTop-i.scrollSpeed);if(!i.axis||i.axis!="y")r.overflowOffset.left+r.scrollParent[0].offsetWidth-t.pageX<i.scrollSensitivity?r.scrollParent[0].scrollLeft=s=r.scrollParent[0].scrollLeft+i.scrollSpeed:t.pageX-r.overflowOffset.left<i.scrollSensitivity&&(r.scrollParent[0].scrollLeft=s=r.scrollParent[0].scrollLeft-i.scrollSpeed)}else{if(!i.axis||i.axis!="x")t.pageY-e(document).scrollTop()<i.scrollSensitivity?s=e(document).scrollTop(e(document).scrollTop()-i.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<i.scrollSensitivity&&(s=e(document).scrollTop(e(document).scrollTop()+i.scrollSpeed));if(!i.axis||i.axis!="y")t.pageX-e(document).scrollLeft()<i.scrollSensitivity?s=e(document).scrollLeft(e(document).scrollLeft()-i.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<i.scrollSensitivity&&(s=e(document).scrollLeft(e(document).scrollLeft()+i.scrollSpeed))}s!==!1&&e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(r,t)}}),e.ui.plugin.add("draggable","snap",{start:function(t,n){var r=e(this).data("draggable"),i=r.options;r.snapElements=[],e(i.snap.constructor!=String?i.snap.items||":data(draggable)":i.snap).each(function(){var t=e(this),n=t.offset();this!=r.element[0]&&r.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:n.top,left:n.left})})},drag:function(t,n){var r=e(this).data("draggable"),i=r.options,s=i.snapTolerance,o=n.offset.left,u=o+r.helperProportions.width,a=n.offset.top,f=a+r.helperProportions.height;for(var l=r.snapElements.length-1;l>=0;l--){var c=r.snapElements[l].left,h=c+r.snapElements[l].width,p=r.snapElements[l].top,d=p+r.snapElements[l].height;if(!(c-s<o&&o<h+s&&p-s<a&&a<d+s||c-s<o&&o<h+s&&p-s<f&&f<d+s||c-s<u&&u<h+s&&p-s<a&&a<d+s||c-s<u&&u<h+s&&p-s<f&&f<d+s)){r.snapElements[l].snapping&&r.options.snap.release&&r.options.snap.release.call(r.element,t,e.extend(r._uiHash(),{snapItem:r.snapElements[l].item})),r.snapElements[l].snapping=!1;continue}if(i.snapMode!="inner"){var v=Math.abs(p-f)<=s,m=Math.abs(d-a)<=s,g=Math.abs(c-u)<=s,y=Math.abs(h-o)<=s;v&&(n.position.top=r._convertPositionTo("relative",{top:p-r.helperProportions.height,left:0}).top-r.margins.top),m&&(n.position.top=r._convertPositionTo("relative",{top:d,left:0}).top-r.margins.top),g&&(n.position.left=r._convertPositionTo("relative",{top:0,left:c-r.helperProportions.width}).left-r.margins.left),y&&(n.position.left=r._convertPositionTo("relative",{top:0,left:h}).left-r.margins.left)}var b=v||m||g||y;if(i.snapMode!="outer"){var v=Math.abs(p-a)<=s,m=Math.abs(d-f)<=s,g=Math.abs(c-o)<=s,y=Math.abs(h-u)<=s;v&&(n.position.top=r._convertPositionTo("relative",{top:p,left:0}).top-r.margins.top),m&&(n.position.top=r._convertPositionTo("relative",{top:d-r.helperProportions.height,left:0}).top-r.margins.top),g&&(n.position.left=r._convertPositionTo("relative",{top:0,left:c}).left-r.margins.left),y&&(n.position.left=r._convertPositionTo("relative",{top:0,left:h-r.helperProportions.width}).left-r.margins.left)}!r.snapElements[l].snapping&&(v||m||g||y||b)&&r.options.snap.snap&&r.options.snap.snap.call(r.element,t,e.extend(r._uiHash(),{snapItem:r.snapElements[l].item})),r.snapElements[l].snapping=v||m||g||y||b}}}),e.ui.plugin.add("draggable","stack",{start:function(t,n){var r=e(this).data("draggable").options,i=e.makeArray(e(r.stack)).sort(function(t,n){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(n).css("zIndex"),10)||0)});if(!i.length)return;var s=parseInt(i[0].style.zIndex)||0;e(i).each(function(e){this.style.zIndex=s+e}),this[0].style.zIndex=s+i.length}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,n){var r=e(n.helper),i=e(this).data("draggable").options;r.css("zIndex")&&(i._zIndex=r.css("zIndex")),r.css("zIndex",i.zIndex)},stop:function(t,n){var r=e(this).data("draggable").options;r._zIndex&&e(n.helper).css("zIndex",r._zIndex)}})})(jQuery);(function(e,t){e.widget("ui.droppable",{version:"1.9.1",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var t=this.options,n=t.accept;this.isover=0,this.isout=1,this.accept=e.isFunction(n)?n:function(e){return e.is(n)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},e.ui.ddmanager.droppables[t.scope]=e.ui.ddmanager.droppables[t.scope]||[],e.ui.ddmanager.droppables[t.scope].push(this),t.addClasses&&this.element.addClass("ui-droppable")},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];for(var n=0;n<t.length;n++)t[n]==this&&t.splice(n,1);this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,n){t=="accept"&&(this.accept=e.isFunction(n)?n:function(e){return e.is(n)}),e.Widget.prototype._setOption.apply(this,arguments)},_activate:function(t){var n=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),n&&this._trigger("activate",t,this.ui(n))},_deactivate:function(t){var n=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),n&&this._trigger("deactivate",t,this.ui(n))},_over:function(t){var n=e.ui.ddmanager.current;if(!n||(n.currentItem||n.element)[0]==this.element[0])return;this.accept.call(this.element[0],n.currentItem||n.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(n)))},_out:function(t){var n=e.ui.ddmanager.current;if(!n||(n.currentItem||n.element)[0]==this.element[0])return;this.accept.call(this.element[0],n.currentItem||n.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(n)))},_drop:function(t,n){var r=n||e.ui.ddmanager.current;if(!r||(r.currentItem||r.element)[0]==this.element[0])return!1;var i=!1;return this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var t=e.data(this,"droppable");if(t.options.greedy&&!t.options.disabled&&t.options.scope==r.options.scope&&t.accept.call(t.element[0],r.currentItem||r.element)&&e.ui.intersect(r,e.extend(t,{offset:t.element.offset()}),t.options.tolerance))return i=!0,!1}),i?!1:this.accept.call(this.element[0],r.currentItem||r.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(r)),this.element):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(t,n,r){if(!n.offset)return!1;var i=(t.positionAbs||t.position.absolute).left,s=i+t.helperProportions.width,o=(t.positionAbs||t.position.absolute).top,u=o+t.helperProportions.height,a=n.offset.left,f=a+n.proportions.width,l=n.offset.top,c=l+n.proportions.height;switch(r){case"fit":return a<=i&&s<=f&&l<=o&&u<=c;case"intersect":return a<i+t.helperProportions.width/2&&s-t.helperProportions.width/2<f&&l<o+t.helperProportions.height/2&&u-t.helperProportions.height/2<c;case"pointer":var h=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,p=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,d=e.ui.isOver(p,h,l,a,n.proportions.height,n.proportions.width);return d;case"touch":return(o>=l&&o<=c||u>=l&&u<=c||o<l&&u>c)&&(i>=a&&i<=f||s>=a&&s<=f||i<a&&s>f);default:return!1}},e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,n){var r=e.ui.ddmanager.droppables[t.options.scope]||[],i=n?n.type:null,s=(t.currentItem||t.element).find(":data(droppable)").andSelf();e:for(var o=0;o<r.length;o++){if(r[o].options.disabled||t&&!r[o].accept.call(r[o].element[0],t.currentItem||t.element))continue;for(var u=0;u<s.length;u++)if(s[u]==r[o].element[0]){r[o].proportions.height=0;continue e}r[o].visible=r[o].element.css("display")!="none";if(!r[o].visible)continue;i=="mousedown"&&r[o]._activate.call(r[o],n),r[o].offset=r[o].element.offset(),r[o].proportions={width:r[o].element[0].offsetWidth,height:r[o].element[0].offsetHeight}}},drop:function(t,n){var r=!1;return e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options)return;!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance)&&(r=this._drop.call(this,n)||r),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,n))}),r},dragStart:function(t,n){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,n)})},drag:function(t,n){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,n),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(this.options.disabled||this.greedyChild||!this.visible)return;var r=e.ui.intersect(t,this,this.options.tolerance),i=!r&&this.isover==1?"isout":r&&this.isover==0?"isover":null;if(!i)return;var s;if(this.options.greedy){var o=this.options.scope,u=this.element.parents(":data(droppable)").filter(function(){return e.data(this,"droppable").options.scope===o});u.length&&(s=e.data(u[0],"droppable"),s.greedyChild=i=="isover"?1:0)}s&&i=="isover"&&(s.isover=0,s.isout=1,s._out.call(s,n)),this[i]=1,this[i=="isout"?"isover":"isout"]=0,this[i=="isover"?"_over":"_out"].call(this,n),s&&i=="isout"&&(s.isout=0,s.isover=1,s._over.call(s,n))})},dragStop:function(t,n){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,n)}}})(jQuery);(function(e,t){e.widget("ui.resizable",e.ui.mouse,{version:"1.9.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var t=this,n=this.options;this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!n.aspectRatio,aspectRatio:n.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:n.helper||n.ghost||n.animate?n.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=n.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var r=this.handles.split(",");this.handles={};for(var i=0;i<r.length;i++){var s=e.trim(r[i]),o="ui-resizable-"+s,u=e('<div class="ui-resizable-handle '+o+'"></div>');u.css({zIndex:n.zIndex}),"se"==s&&u.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(u)}}this._renderAxis=function(t){t=t||this.element;for(var n in this.handles){this.handles[n].constructor==String&&(this.handles[n]=e(this.handles[n],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var r=e(this.handles[n],this.element),i=0;i=/sw|ne|nw|se|n|s/.test(n)?r.outerHeight():r.outerWidth();var s=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");t.css(s,i),this._proportionallyResize()}if(!e(this.handles[n]).length)continue}},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!t.resizing){if(this.className)var e=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);t.axis=e&&e[1]?e[1]:"se"}}),n.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){if(n.disabled)return;e(this).removeClass("ui-resizable-autohide"),t._handles.show()}).mouseleave(function(){if(n.disabled)return;t.resizing||(e(this).addClass("ui-resizable-autohide"),t._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){t(this.element);var n=this.element;this.originalElement.css({position:n.css("position"),width:n.outerWidth(),height:n.outerHeight(),top:n.css("top"),left:n.css("left")}).insertAfter(n),n.remove()}return this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_mouseCapture:function(t){var n=!1;for(var r in this.handles)e(this.handles[r])[0]==t.target&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var r=this.options,i=this.element.position(),s=this.element;this.resizing=!0,this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()},(s.is(".ui-draggable")||/absolute/.test(s.css("position")))&&s.css({position:"absolute",top:i.top,left:i.left}),this._renderProxy();var o=n(this.helper.css("left")),u=n(this.helper.css("top"));r.containment&&(o+=e(r.containment).scrollLeft()||0,u+=e(r.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:o,top:u},this.size=this._helper?{width:s.outerWidth(),height:s.outerHeight()}:{width:s.width(),height:s.height()},this.originalSize=this._helper?{width:s.outerWidth(),height:s.outerHeight()}:{width:s.width(),height:s.height()},this.originalPosition={left:o,top:u},this.sizeDiff={width:s.outerWidth()-s.width(),height:s.outerHeight()-s.height()},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio=typeof r.aspectRatio=="number"?r.aspectRatio:this.originalSize.width/this.originalSize.height||1;var a=e(".ui-resizable-"+this.axis).css("cursor");return e("body").css("cursor",a=="auto"?this.axis+"-resize":a),s.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(e){var t=this.helper,n=this.options,r={},i=this,s=this.originalMousePosition,o=this.axis,u=e.pageX-s.left||0,a=e.pageY-s.top||0,f=this._change[o];if(!f)return!1;var l=f.apply(this,[e,u,a]);this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey)l=this._updateRatio(l,e);return l=this._respectSize(l,e),this._propagate("resize",e),t.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",e,this.ui()),!1},_mouseStop:function(t){this.resizing=!1;var n=this.options,r=this;if(this._helper){var i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),o=s&&e.ui.hasScroll(i[0],"left")?0:r.sizeDiff.height,u=s?0:r.sizeDiff.width,a={width:r.helper.width()-u,height:r.helper.height()-o},f=parseInt(r.element.css("left"),10)+(r.position.left-r.originalPosition.left)||null,l=parseInt(r.element.css("top"),10)+(r.position.top-r.originalPosition.top)||null;n.animate||this.element.css(e.extend(a,{top:l,left:f})),r.helper.height(r.size.height),r.helper.width(r.size.width),this._helper&&!n.animate&&this._proportionallyResize()}return e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t=this.options,n,i,s,o,u;u={minWidth:r(t.minWidth)?t.minWidth:0,maxWidth:r(t.maxWidth)?t.maxWidth:Infinity,minHeight:r(t.minHeight)?t.minHeight:0,maxHeight:r(t.maxHeight)?t.maxHeight:Infinity};if(this._aspectRatio||e)n=u.minHeight*this.aspectRatio,s=u.minWidth/this.aspectRatio,i=u.maxHeight*this.aspectRatio,o=u.maxWidth/this.aspectRatio,n>u.minWidth&&(u.minWidth=n),s>u.minHeight&&(u.minHeight=s),i<u.maxWidth&&(u.maxWidth=i),o<u.maxHeight&&(u.maxHeight=o);this._vBoundaries=u},_updateCache:function(e){var t=this.options;this.offset=this.helper.offset(),r(e.left)&&(this.position.left=e.left),r(e.top)&&(this.position.top=e.top),r(e.height)&&(this.size.height=e.height),r(e.width)&&(this.size.width=e.width)},_updateRatio:function(e,t){var n=this.options,i=this.position,s=this.size,o=this.axis;return r(e.height)?e.width=e.height*this.aspectRatio:r(e.width)&&(e.height=e.width/this.aspectRatio),o=="sw"&&(e.left=i.left+(s.width-e.width),e.top=null),o=="nw"&&(e.top=i.top+(s.height-e.height),e.left=i.left+(s.width-e.width)),e},_respectSize:function(e,t){var n=this.helper,i=this._vBoundaries,s=this._aspectRatio||t.shiftKey,o=this.axis,u=r(e.width)&&i.maxWidth&&i.maxWidth<e.width,a=r(e.height)&&i.maxHeight&&i.maxHeight<e.height,f=r(e.width)&&i.minWidth&&i.minWidth>e.width,l=r(e.height)&&i.minHeight&&i.minHeight>e.height;f&&(e.width=i.minWidth),l&&(e.height=i.minHeight),u&&(e.width=i.maxWidth),a&&(e.height=i.maxHeight);var c=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,p=/sw|nw|w/.test(o),d=/nw|ne|n/.test(o);f&&p&&(e.left=c-i.minWidth),u&&p&&(e.left=c-i.maxWidth),l&&d&&(e.top=h-i.minHeight),a&&d&&(e.top=h-i.maxHeight);var v=!e.width&&!e.height;return v&&!e.left&&e.top?e.top=null:v&&!e.top&&e.left&&(e.left=null),e},_proportionallyResize:function(){var t=this.options;if(!this._proportionallyResizeElements.length)return;var n=this.helper||this.element;for(var r=0;r<this._proportionallyResizeElements.length;r++){var i=this._proportionallyResizeElements[r];if(!this.borderDif){var s=[i.css("borderTopWidth"),i.css("borderRightWidth"),i.css("borderBottomWidth"),i.css("borderLeftWidth")],o=[i.css("paddingTop"),i.css("paddingRight"),i.css("paddingBottom"),i.css("paddingLeft")];this.borderDif=e.map(s,function(e,t){var n=parseInt(e,10)||0,r=parseInt(o[t],10)||0;return n+r})}i.css({height:n.height()-this.borderDif[0]-this.borderDif[2]||0,width:n.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var t=this.element,n=this.options;this.elementOffset=t.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var r=e.ui.ie6?1:0,i=e.ui.ie6?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+i,height:this.element.outerHeight()+i,position:"absolute",left:this.elementOffset.left-r+"px",top:this.elementOffset.top-r+"px",zIndex:++n.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(e,t,n){return{width:this.originalSize.width+t}},w:function(e,t,n){var r=this.options,i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,n){var r=this.options,i=this.originalSize,s=this.originalPosition;return{top:s.top+n,height:i.height-n}},s:function(e,t,n){return{height:this.originalSize.height+n}},se:function(t,n,r){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,n,r]))},sw:function(t,n,r){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,n,r]))},ne:function(t,n,r){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,n,r]))},nw:function(t,n,r){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,n,r]))}},_propagate:function(t,n){e.ui.plugin.call(this,t,[n,this.ui()]),t!="resize"&&this._trigger(t,n,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","alsoResize",{start:function(t,n){var r=e(this).data("resizable"),i=r.options,s=function(t){e(t).each(function(){var t=e(this);t.data("resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};typeof i.alsoResize=="object"&&!i.alsoResize.parentNode?i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)}):s(i.alsoResize)},resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.originalSize,o=r.originalPosition,u={height:r.size.height-s.height||0,width:r.size.width-s.width||0,top:r.position.top-o.top||0,left:r.position.left-o.left||0},a=function(t,r){e(t).each(function(){var t=e(this),i=e(this).data("resizable-alsoresize"),s={},o=r&&r.length?r:t.parents(n.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var n=(i[t]||0)+(u[t]||0);n&&n>=0&&(s[t]=n||null)}),t.css(s)})};typeof i.alsoResize=="object"&&!i.alsoResize.nodeType?e.each(i.alsoResize,function(e,t){a(e,t)}):a(i.alsoResize)},stop:function(t,n){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","animate",{stop:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r._proportionallyResizeElements,o=s.length&&/textarea/i.test(s[0].nodeName),u=o&&e.ui.hasScroll(s[0],"left")?0:r.sizeDiff.height,a=o?0:r.sizeDiff.width,f={width:r.size.width-a,height:r.size.height-u},l=parseInt(r.element.css("left"),10)+(r.position.left-r.originalPosition.left)||null,c=parseInt(r.element.css("top"),10)+(r.position.top-r.originalPosition.top)||null;r.element.animate(e.extend(f,c&&l?{top:c,left:l}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var n={width:parseInt(r.element.css("width"),10),height:parseInt(r.element.css("height"),10),top:parseInt(r.element.css("top"),10),left:parseInt(r.element.css("left"),10)};s&&s.length&&e(s[0]).css({width:n.width,height:n.height}),r._updateCache(n),r._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(t,r){var i=e(this).data("resizable"),s=i.options,o=i.element,u=s.containment,a=u instanceof e?u.get(0):/parent/.test(u)?o.parent().get(0):u;if(!a)return;i.containerElement=e(a);if(/document/.test(u)||u==document)i.containerOffset={left:0,top:0},i.containerPosition={left:0,top:0},i.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight};else{var f=e(a),l=[];e(["Top","Right","Left","Bottom"]).each(function(e,t){l[e]=n(f.css("padding"+t))}),i.containerOffset=f.offset(),i.containerPosition=f.position(),i.containerSize={height:f.innerHeight()-l[3],width:f.innerWidth()-l[1]};var c=i.containerOffset,h=i.containerSize.height,p=i.containerSize.width,d=e.ui.hasScroll(a,"left")?a.scrollWidth:p,v=e.ui.hasScroll(a)?a.scrollHeight:h;i.parentData={element:a,left:c.left,top:c.top,width:d,height:v}}},resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.containerSize,o=r.containerOffset,u=r.size,a=r.position,f=r._aspectRatio||t.shiftKey,l={top:0,left:0},c=r.containerElement;c[0]!=document&&/static/.test(c.css("position"))&&(l=o),a.left<(r._helper?o.left:0)&&(r.size.width=r.size.width+(r._helper?r.position.left-o.left:r.position.left-l.left),f&&(r.size.height=r.size.width/r.aspectRatio),r.position.left=i.helper?o.left:0),a.top<(r._helper?o.top:0)&&(r.size.height=r.size.height+(r._helper?r.position.top-o.top:r.position.top),f&&(r.size.width=r.size.height*r.aspectRatio),r.position.top=r._helper?o.top:0),r.offset.left=r.parentData.left+r.position.left,r.offset.top=r.parentData.top+r.position.top;var h=Math.abs((r._helper?r.offset.left-l.left:r.offset.left-l.left)+r.sizeDiff.width),p=Math.abs((r._helper?r.offset.top-l.top:r.offset.top-o.top)+r.sizeDiff.height),d=r.containerElement.get(0)==r.element.parent().get(0),v=/relative|absolute/.test(r.containerElement.css("position"));d&&v&&(h-=r.parentData.left),h+r.size.width>=r.parentData.width&&(r.size.width=r.parentData.width-h,f&&(r.size.height=r.size.width/r.aspectRatio)),p+r.size.height>=r.parentData.height&&(r.size.height=r.parentData.height-p,f&&(r.size.width=r.size.height*r.aspectRatio))},stop:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.position,o=r.containerOffset,u=r.containerPosition,a=r.containerElement,f=e(r.helper),l=f.offset(),c=f.outerWidth()-r.sizeDiff.width,h=f.outerHeight()-r.sizeDiff.height;r._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:l.left-u.left-o.left,width:c,height:h}),r._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:l.left-u.left-o.left,width:c,height:h})}}),e.ui.plugin.add("resizable","ghost",{start:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.size;r.ghost=r.originalElement.clone(),r.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof i.ghost=="string"?i.ghost:""),r.ghost.appendTo(r.helper)},resize:function(t,n){var r=e(this).data("resizable"),i=r.options;r.ghost&&r.ghost.css({position:"relative",height:r.size.height,width:r.size.width})},stop:function(t,n){var r=e(this).data("resizable"),i=r.options;r.ghost&&r.helper&&r.helper.get(0).removeChild(r.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.size,o=r.originalSize,u=r.originalPosition,a=r.axis,f=i._aspectRatio||t.shiftKey;i.grid=typeof i.grid=="number"?[i.grid,i.grid]:i.grid;var l=Math.round((s.width-o.width)/(i.grid[0]||1))*(i.grid[0]||1),c=Math.round((s.height-o.height)/(i.grid[1]||1))*(i.grid[1]||1);/^(se|s|e)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c):/^(ne)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c,r.position.top=u.top-c):/^(sw)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c,r.position.left=u.left-l):(r.size.width=o.width+l,r.size.height=o.height+c,r.position.top=u.top-c,r.position.left=u.left-l)}});var n=function(e){return parseInt(e,10)||0},r=function(e){return!isNaN(parseInt(e,10))}})(jQuery);(function(e,t){e.widget("ui.selectable",e.ui.mouse,{version:"1.9.1",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var t=this;this.element.addClass("ui-selectable"),this.dragged=!1;var n;this.refresh=function(){n=e(t.options.filter,t.element[0]),n.addClass("ui-selectee"),n.each(function(){var t=e(this),n=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:n.left,top:n.top,right:n.left+t.outerWidth(),bottom:n.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=n.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var n=this;this.opos=[t.pageX,t.pageY];if(this.options.disabled)return;var r=this.options;this.selectees=e(r.filter,this.element[0]),this._trigger("start",t),e(r.appendTo).append(this.helper),this.helper.css({left:t.clientX,top:t.clientY,width:0,height:0}),r.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var r=e.data(this,"selectable-item");r.startselected=!0,!t.metaKey&&!t.ctrlKey&&(r.$element.removeClass("ui-selected"),r.selected=!1,r.$element.addClass("ui-unselecting"),r.unselecting=!0,n._trigger("unselecting",t,{unselecting:r.element}))}),e(t.target).parents().andSelf().each(function(){var r=e.data(this,"selectable-item");if(r){var i=!t.metaKey&&!t.ctrlKey||!r.$element.hasClass("ui-selected");return r.$element.removeClass(i?"ui-unselecting":"ui-selected").addClass(i?"ui-selecting":"ui-unselecting"),r.unselecting=!i,r.selecting=i,r.selected=i,i?n._trigger("selecting",t,{selecting:r.element}):n._trigger("unselecting",t,{unselecting:r.element}),!1}})},_mouseDrag:function(t){var n=this;this.dragged=!0;if(this.options.disabled)return;var r=this.options,i=this.opos[0],s=this.opos[1],o=t.pageX,u=t.pageY;if(i>o){var a=o;o=i,i=a}if(s>u){var a=u;u=s,s=a}return this.helper.css({left:i,top:s,width:o-i,height:u-s}),this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!a||a.element==n.element[0])return;var f=!1;r.tolerance=="touch"?f=!(a.left>o||a.right<i||a.top>u||a.bottom<s):r.tolerance=="fit"&&(f=a.left>i&&a.right<o&&a.top>s&&a.bottom<u),f?(a.selected&&(a.$element.removeClass("ui-selected"),a.selected=!1),a.unselecting&&(a.$element.removeClass("ui-unselecting"),a.unselecting=!1),a.selecting||(a.$element.addClass("ui-selecting"),a.selecting=!0,n._trigger("selecting",t,{selecting:a.element}))):(a.selecting&&((t.metaKey||t.ctrlKey)&&a.startselected?(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.$element.addClass("ui-selected"),a.selected=!0):(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.startselected&&(a.$element.addClass("ui-unselecting"),a.unselecting=!0),n._trigger("unselecting",t,{unselecting:a.element}))),a.selected&&!t.metaKey&&!t.ctrlKey&&!a.startselected&&(a.$element.removeClass("ui-selected"),a.selected=!1,a.$element.addClass("ui-unselecting"),a.unselecting=!0,n._trigger("unselecting",t,{unselecting:a.element})))}),!1},_mouseStop:function(t){var n=this;this.dragged=!1;var r=this.options;return e(".ui-unselecting",this.element[0]).each(function(){var r=e.data(this,"selectable-item");r.$element.removeClass("ui-unselecting"),r.unselecting=!1,r.startselected=!1,n._trigger("unselected",t,{unselected:r.element})}),e(".ui-selecting",this.element[0]).each(function(){var r=e.data(this,"selectable-item");r.$element.removeClass("ui-selecting").addClass("ui-selected"),r.selecting=!1,r.selected=!0,r.startselected=!0,n._trigger("selected",t,{selected:r.element})}),this._trigger("stop",t),this.helper.remove(),!1}})})(jQuery);(function(e,t){e.widget("ui.sortable",e.ui.mouse,{version:"1.9.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?e.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_setOption:function(t,n){t==="disabled"?(this.options[t]=n,this.widget().toggleClass("ui-sortable-disabled",!!n)):e.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(t,n){var r=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(t);var i=null,s=e(t.target).parents().each(function(){if(e.data(this,r.widgetName+"-item")==r)return i=e(this),!1});e.data(t.target,r.widgetName+"-item")==r&&(i=e(t.target));if(!i)return!1;if(this.options.handle&&!n){var o=!1;e(this.options.handle,i).find("*").andSelf().each(function(){this==t.target&&(o=!0)});if(!o)return!1}return this.currentItem=i,this._removeCurrentsFromItems(),!0},_mouseStart:function(t,n,r){var i=this.options;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),i.containment&&this._setContainment(),i.cursor&&(e("body").css("cursor")&&(this._storedCursor=e("body").css("cursor")),e("body").css("cursor",i.cursor)),i.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",i.opacity)),i.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",i.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!r)for(var s=this.containers.length-1;s>=0;s--)this.containers[s]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var n=this.options,r=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<n.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+n.scrollSpeed:t.pageY-this.overflowOffset.top<n.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-n.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<n.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+n.scrollSpeed:t.pageX-this.overflowOffset.left<n.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-n.scrollSpeed)):(t.pageY-e(document).scrollTop()<n.scrollSensitivity?r=e(document).scrollTop(e(document).scrollTop()-n.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<n.scrollSensitivity&&(r=e(document).scrollTop(e(document).scrollTop()+n.scrollSpeed)),t.pageX-e(document).scrollLeft()<n.scrollSensitivity?r=e(document).scrollLeft(e(document).scrollLeft()-n.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<n.scrollSensitivity&&(r=e(document).scrollLeft(e(document).scrollLeft()+n.scrollSpeed))),r!==!1&&e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var i=this.items.length-1;i>=0;i--){var s=this.items[i],o=s.item[0],u=this._intersectsWithPointer(s);if(!u)continue;if(s.instance!==this.currentContainer)continue;if(o!=this.currentItem[0]&&this.placeholder[u==1?"next":"prev"]()[0]!=o&&!e.contains(this.placeholder[0],o)&&(this.options.type=="semi-dynamic"?!e.contains(this.element[0],o):!0)){this.direction=u==1?"down":"up";if(this.options.tolerance!="pointer"&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,n){if(!t)return;e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t);if(this.options.revert){var r=this,i=this.placeholder.offset();this.reverting=!0,e(this.helper).animate({left:i.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:i.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){r._clear(t)})}else this._clear(t,n);return!1},cancel:function(){if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var n=this._getItemsAsjQuery(t&&t.connected),r=[];return t=t||{},e(n).each(function(){var n=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[-=_](.+)/);n&&r.push((t.key||n[1]+"[]")+"="+(t.key&&t.expression?n[1]:n[2]))}),!r.length&&t.key&&r.push(t.key+"="),r.join("&")},toArray:function(t){var n=this._getItemsAsjQuery(t&&t.connected),r=[];return t=t||{},n.each(function(){r.push(e(t.item||this).attr(t.attribute||"id")||"")}),r},_intersectsWith:function(e){var t=this.positionAbs.left,n=t+this.helperProportions.width,r=this.positionAbs.top,i=r+this.helperProportions.height,s=e.left,o=s+e.width,u=e.top,a=u+e.height,f=this.offset.click.top,l=this.offset.click.left,c=r+f>u&&r+f<a&&t+l>s&&t+l<o;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?c:s<t+this.helperProportions.width/2&&n-this.helperProportions.width/2<o&&u<r+this.helperProportions.height/2&&i-this.helperProportions.height/2<a},_intersectsWithPointer:function(t){var n=this.options.axis==="x"||e.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),r=this.options.axis==="y"||e.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),i=n&&r,s=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return i?this.floating?o&&o=="right"||s=="down"?2:1:s&&(s=="down"?2:1):!1},_intersectsWithSides:function(t){var n=e.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),r=e.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),i=this._getDragVerticalDirection(),s=this._getDragHorizontalDirection();return this.floating&&s?s=="right"&&r||s=="left"&&!r:i&&(i=="down"&&n||i=="up"&&!n)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return e!=0&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return e!=0&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor==String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){var n=[],r=[],i=this._connectWith();if(i&&t)for(var s=i.length-1;s>=0;s--){var o=e(i[s]);for(var u=o.length-1;u>=0;u--){var a=e.data(o[u],this.widgetName);a&&a!=this&&!a.options.disabled&&r.push([e.isFunction(a.options.items)?a.options.items.call(a.element):e(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a])}}r.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var s=r.length-1;s>=0;s--)r[s][0].each(function(){n.push(this)});return e(n)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var n=0;n<t.length;n++)if(t[n]==e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var n=this.items,r=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],i=this._connectWith();if(i&&this.ready)for(var s=i.length-1;s>=0;s--){var o=e(i[s]);for(var u=o.length-1;u>=0;u--){var a=e.data(o[u],this.widgetName);a&&a!=this&&!a.options.disabled&&(r.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a))}}for(var s=r.length-1;s>=0;s--){var f=r[s][1],l=r[s][0];for(var u=0,c=l.length;u<c;u++){var h=e(l[u]);h.data(this.widgetName+"-item",f),n.push({item:h,instance:f,width:0,height:0,left:0,top:0})}}},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var n=this.items.length-1;n>=0;n--){var r=this.items[n];if(r.instance!=this.currentContainer&&this.currentContainer&&r.item[0]!=this.currentItem[0])continue;var i=this.options.toleranceElement?e(this.options.toleranceElement,r.item):r.item;t||(r.width=i.outerWidth(),r.height=i.outerHeight());var s=i.offset();r.left=s.left,r.top=s.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var n=this.containers.length-1;n>=0;n--){var s=this.containers[n].element.offset();this.containers[n].containerCache.left=s.left,this.containers[n].containerCache.top=s.top,this.containers[n].containerCache.width=this.containers[n].element.outerWidth(),this.containers[n].containerCache.height=this.containers[n].element.outerHeight()}return this},_createPlaceholder:function(t){t=t||this;var n=t.options;if(!n.placeholder||n.placeholder.constructor==String){var r=n.placeholder;n.placeholder={element:function(){var n=e(document.createElement(t.currentItem[0].nodeName)).addClass(r||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];return r||(n.style.visibility="hidden"),n},update:function(e,i){if(r&&!n.forcePlaceholderSize)return;i.height()||i.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),i.width()||i.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10))}}}t.placeholder=e(n.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),n.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var n=null,r=null;for(var i=this.containers.length-1;i>=0;i--){if(e.contains(this.currentItem[0],this.containers[i].element[0]))continue;if(this._intersectsWith(this.containers[i].containerCache)){if(n&&e.contains(this.containers[i].element[0],n.element[0]))continue;n=this.containers[i],r=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0)}if(!n)return;if(this.containers.length===1)this.containers[r]._trigger("over",t,this._uiHash(this)),this.containers[r].containerCache.over=1;else{var s=1e4,o=null,u=this.containers[r].floating?"left":"top",a=this.containers[r].floating?"width":"height",f=this.positionAbs[u]+this.offset.click[u];for(var l=this.items.length-1;l>=0;l--){if(!e.contains(this.containers[r].element[0],this.items[l].item[0]))continue;if(this.items[l].item[0]==this.currentItem[0])continue;var c=this.items[l].item.offset()[u],h=!1;Math.abs(c-f)>Math.abs(c+this.items[l][a]-f)&&(h=!0,c+=this.items[l][a]),Math.abs(c-f)<s&&(s=Math.abs(c-f),o=this.items[l],this.direction=h?"up":"down")}if(!o&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[r],o?this._rearrange(t,o,null,!0):this._rearrange(t,null,this.containers[r].element,!0),this._trigger("change",t,this._uiHash()),this.containers[r]._trigger("change",t,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[r]._trigger("over",t,this._uiHash(this)),this.containers[r].containerCache.over=1}},_createHelper:function(t){var n=this.options,r=e.isFunction(n.helper)?e(n.helper.apply(this.element[0],[t,this.currentItem])):n.helper=="clone"?this.currentItem.clone():this.currentItem;return r.parents("body").length||e(n.appendTo!="parent"?n.appendTo:this.currentItem[0].parentNode)[0].appendChild(r[0]),r[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(r[0].style.width==""||n.forceHelperSize)&&r.width(this.currentItem.width()),(r[0].style.height==""||n.forceHelperSize)&&r.height(this.currentItem.height()),r},_adjustOffsetFromHelper:function(t){typeof t=="string"&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&e.ui.ie)t={top:0,left:0};return{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t=this.options;t.containment=="parent"&&(t.containment=this.helper[0].parentNode);if(t.containment=="document"||t.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e(t.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(e(t.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(t.containment)){var n=e(t.containment)[0],r=e(t.containment).offset(),i=e(n).css("overflow")!="hidden";this.containment=[r.left+(parseInt(e(n).css("borderLeftWidth"),10)||0)+(parseInt(e(n).css("paddingLeft"),10)||0)-this.margins.left,r.top+(parseInt(e(n).css("borderTopWidth"),10)||0)+(parseInt(e(n).css("paddingTop"),10)||0)-this.margins.top,r.left+(i?Math.max(n.scrollWidth,n.offsetWidth):n.offsetWidth)-(parseInt(e(n).css("borderLeftWidth"),10)||0)-(parseInt(e(n).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,r.top+(i?Math.max(n.scrollHeight,n.offsetHeight):n.offsetHeight)-(parseInt(e(n).css("borderTopWidth"),10)||0)-(parseInt(e(n).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(t,n){n||(n=this.position);var r=t=="absolute"?1:-1,i=this.options,s=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(s[0].tagName);return{top:n.top+this.offset.relative.top*r+this.offset.parent.top*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():o?0:s.scrollTop())*r,left:n.left+this.offset.relative.left*r+this.offset.parent.left*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():o?0:s.scrollLeft())*r}},_generatePosition:function(t){var n=this.options,r=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,i=/(html|body)/i.test(r[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var s=t.pageX,o=t.pageY;if(this.originalPosition){this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(s=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(o=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(s=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top));if(n.grid){var u=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1];o=this.containment?u-this.offset.click.top<this.containment[1]||u-this.offset.click.top>this.containment[3]?u-this.offset.click.top<this.containment[1]?u+n.grid[1]:u-n.grid[1]:u:u;var a=this.originalPageX+Math.round((s-this.originalPageX)/n.grid[0])*n.grid[0];s=this.containment?a-this.offset.click.left<this.containment[0]||a-this.offset.click.left>this.containment[2]?a-this.offset.click.left<this.containment[0]?a+n.grid[0]:a-n.grid[0]:a:a}}return{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():i?0:r.scrollTop()),left:s-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():i?0:r.scrollLeft())}},_rearrange:function(e,t,n,r){n?n[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var i=this.counter;this._delay(function(){i==this.counter&&this.refreshPositions(!r)})},_clear:function(t,n){this.reverting=!1;var r=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var i in this._storedCSS)if(this._storedCSS[i]=="auto"||this._storedCSS[i]=="static")this._storedCSS[i]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!n&&r.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!n&&r.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(n||(r.push(function(e){this._trigger("remove",e,this._uiHash())}),r.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),r.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer))));for(var i=this.containers.length-1;i>=0;i--)n||r.push(function(e){return function(t){e._trigger("deactivate",t,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over&&(r.push(function(e){return function(t){e._trigger("out",t,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over=0);this._storedCursor&&e("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!n){this._trigger("beforeStop",t,this._uiHash());for(var i=0;i<r.length;i++)r[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}n||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!n){for(var i=0;i<r.length;i++)r[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var n=t||this;return{helper:n.helper,placeholder:n.placeholder||e([]),position:n.position,originalPosition:n.originalPosition,offset:n.positionAbs,item:n.currentItem,sender:t?t.element:null}}})})(jQuery);(function(e,t){var n=0,r={},i={};r.height=r.paddingTop=r.paddingBottom=r.borderTopWidth=r.borderBottomWidth="hide",i.height=i.paddingTop=i.paddingBottom=i.borderTopWidth=i.borderBottomWidth="show",e.widget("ui.accordion",{version:"1.9.1",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},_create:function(){var t=this.accordionId="ui-accordion-"+(this.element.attr("id")||++n),r=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset"),this.headers=this.element.find(r.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all"),this._hoverable(this.headers),this._focusable(this.headers),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").hide(),!r.collapsible&&(r.active===!1||r.active==null)&&(r.active=0),r.active<0&&(r.active+=this.headers.length),this.active=this._findActive(r.active).addClass("ui-accordion-header-active ui-state-active").toggleClass("ui-corner-all ui-corner-top"),this.active.next().addClass("ui-accordion-content-active").show(),this._createIcons(),this.refresh(),this.element.attr("role","tablist"),this.headers.attr("role","tab").each(function(n){var r=e(this),i=r.attr("id"),s=r.next(),o=s.attr("id");i||(i=t+"-header-"+n,r.attr("id",i)),o||(o=t+"-panel-"+n,s.attr("id",o)),r.attr("aria-controls",o),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false",tabIndex:-1}).next().attr({"aria-expanded":"false","aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true",tabIndex:0}).next().attr({"aria-expanded":"true","aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._on(this.headers,{keydown:"_keydown"}),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._setupEvents(r.event)},_getCreateEventData:function(){return{header:this.active,content:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),this._destroyIcons(),e=this.headers.next().css("display","").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),this.options.heightStyle!=="content"&&e.css("height","")},_setOption:function(e,t){if(e==="active"){this._activate(t);return}e==="event"&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),e==="collapsible"&&!t&&this.options.active===!1&&this._activate(0),e==="icons"&&(this._destroyIcons(),t&&this._createIcons()),e==="disabled"&&this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)},_keydown:function(t){if(t.altKey||t.ctrlKey)return;var n=e.ui.keyCode,r=this.headers.length,i=this.headers.index(t.target),s=!1;switch(t.keyCode){case n.RIGHT:case n.DOWN:s=this.headers[(i+1)%r];break;case n.LEFT:case n.UP:s=this.headers[(i-1+r)%r];break;case n.SPACE:case n.ENTER:this._eventHandler(t);break;case n.HOME:s=this.headers[0];break;case n.END:s=this.headers[r-1]}s&&(e(t.target).attr("tabIndex",-1),e(s).attr("tabIndex",0),s.focus(),t.preventDefault())},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t,n,r=this.options.heightStyle,i=this.element.parent();r==="fill"?(e.support.minHeight||(n=i.css("overflow"),i.css("overflow","hidden")),t=i.height(),this.element.siblings(":visible").each(function(){var n=e(this),r=n.css("position");if(r==="absolute"||r==="fixed")return;t-=n.outerHeight(!0)}),n&&i.css("overflow",n),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):r==="auto"&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).height("").height())}).height(t))},_activate:function(t){var n=this._findActive(t)[0];if(n===this.active[0])return;n=n||this.active[0],this._eventHandler({target:n,currentTarget:n,preventDefault:e.noop})},_findActive:function(t){return typeof t=="number"?this.headers.eq(t):e()},_setupEvents:function(t){var n={};if(!t)return;e.each(t.split(" "),function(e,t){n[t]="_eventHandler"}),this._on(this.headers,n)},_eventHandler:function(t){var n=this.options,r=this.active,i=e(t.currentTarget),s=i[0]===r[0],o=s&&n.collapsible,u=o?e():i.next(),a=r.next(),f={oldHeader:r,oldPanel:a,newHeader:o?e():i,newPanel:u};t.preventDefault();if(s&&!n.collapsible||this._trigger("beforeActivate",t,f)===!1)return;n.active=o?!1:this.headers.index(i),this.active=s?e():i,this._toggle(f),r.removeClass("ui-accordion-header-active ui-state-active"),n.icons&&r.children(".ui-accordion-header-icon").removeClass(n.icons.activeHeader).addClass(n.icons.header),s||(i.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),n.icons&&i.children(".ui-accordion-header-icon").removeClass(n.icons.header).addClass(n.icons.activeHeader),i.next().addClass("ui-accordion-content-active"))},_toggle:function(t){var n=t.newPanel,r=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=n,this.prevHide=r,this.options.animate?this._animate(n,r,t):(r.hide(),n.show(),this._toggleComplete(t)),r.attr({"aria-expanded":"false","aria-hidden":"true"}),r.prev().attr("aria-selected","false"),n.length&&r.length?r.prev().attr("tabIndex",-1):n.length&&this.headers.filter(function(){return e(this).attr("tabIndex")===0}).attr("tabIndex",-1),n.attr({"aria-expanded":"true","aria-hidden":"false"}).prev().attr({"aria-selected":"true",tabIndex:0})},_animate:function(e,t,n){var s,o,u,a=this,f=0,l=e.length&&(!t.length||e.index()<t.index()),c=this.options.animate||{},h=l&&c.down||c,p=function(){a._toggleComplete(n)};typeof h=="number"&&(u=h),typeof h=="string"&&(o=h),o=o||h.easing||c.easing,u=u||h.duration||c.duration;if(!t.length)return e.animate(i,u,o,p);if(!e.length)return t.animate(r,u,o,p);s=e.show().outerHeight(),t.animate(r,{duration:u,easing:o,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(i,{duration:u,easing:o,complete:p,step:function(e,n){n.now=Math.round(e),n.prop!=="height"?f+=n.now:a.options.heightStyle!=="content"&&(n.now=Math.round(s-t.outerHeight()-f),f=0)}})},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}}),e.uiBackCompat!==!1&&(function(e,t){e.extend(t.options,{navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}});var n=t._create;t._create=function(){if(this.options.navigation){var t=this,r=this.element.find(this.options.header),i=r.next(),s=r.add(i).find("a").filter(this.options.navigationFilter)[0];s&&r.add(i).each(function(n){if(e.contains(this,s))return t.options.active=Math.floor(n/2),!1})}n.call(this)}}(jQuery,jQuery.ui.accordion.prototype),function(e,t){e.extend(t.options,{heightStyle:null,autoHeight:!0,clearStyle:!1,fillSpace:!1});var n=t._create,r=t._setOption;e.extend(t,{_create:function(){this.options.heightStyle=this.options.heightStyle||this._mergeHeightStyle(),n.call(this)},_setOption:function(e){if(e==="autoHeight"||e==="clearStyle"||e==="fillSpace")this.options.heightStyle=this._mergeHeightStyle();r.apply(this,arguments)},_mergeHeightStyle:function(){var e=this.options;if(e.fillSpace)return"fill";if(e.clearStyle)return"content";if(e.autoHeight)return"auto"}})}(jQuery,jQuery.ui.accordion.prototype),function(e,t){e.extend(t.options.icons,{activeHeader:null,headerSelected:"ui-icon-triangle-1-s"});var n=t._createIcons;t._createIcons=function(){this.options.icons&&(this.options.icons.activeHeader=this.options.icons.activeHeader||this.options.icons.headerSelected),n.call(this)}}(jQuery,jQuery.ui.accordion.prototype),function(e,t){t.activate=t._activate;var n=t._findActive;t._findActive=function(e){return e===-1&&(e=!1),e&&typeof e!="number"&&(e=this.headers.index(this.headers.filter(e)),e===-1&&(e=!1)),n.call(this,e)}}(jQuery,jQuery.ui.accordion.prototype),jQuery.ui.accordion.prototype.resize=jQuery.ui.accordion.prototype.refresh,function(e,t){e.extend(t.options,{change:null,changestart:null});var n=t._trigger;t._trigger=function(e,t,r){var i=n.apply(this,arguments);return i?(e==="beforeActivate"?i=n.call(this,"changestart",t,{oldHeader:r.oldHeader,oldContent:r.oldPanel,newHeader:r.newHeader,newContent:r.newPanel}):e==="activate"&&(i=n.call(this,"change",t,{oldHeader:r.oldHeader,oldContent:r.oldPanel,newHeader:r.newHeader,newContent:r.newPanel})),i):!1}}(jQuery,jQuery.ui.accordion.prototype),function(e,t){e.extend(t.options,{animate:null,animated:"slide"});var n=t._create;t._create=function(){var e=this.options;e.animate===null&&(e.animated?e.animated==="slide"?e.animate=300:e.animated==="bounceslide"?e.animate={duration:200,down:{easing:"easeOutBounce",duration:1e3}}:e.animate=e.animated:e.animate=!1),n.call(this)}}(jQuery,jQuery.ui.accordion.prototype))})(jQuery);(function(e,t){var n=0;e.widget("ui.autocomplete",{version:"1.9.1",defaultElement:"<input>",options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},pending:0,_create:function(){var t,n,r;this.isMultiLine=this._isMultiLine(),this.valueMethod=this.element[this.element.is("input,textarea")?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(i){if(this.element.prop("readOnly")){t=!0,r=!0,n=!0;return}t=!1,r=!1,n=!1;var s=e.ui.keyCode;switch(i.keyCode){case s.PAGE_UP:t=!0,this._move("previousPage",i);break;case s.PAGE_DOWN:t=!0,this._move("nextPage",i);break;case s.UP:t=!0,this._keyEvent("previous",i);break;case s.DOWN:t=!0,this._keyEvent("next",i);break;case s.ENTER:case s.NUMPAD_ENTER:this.menu.active&&(t=!0,i.preventDefault(),this.menu.select(i));break;case s.TAB:this.menu.active&&this.menu.select(i);break;case s.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(i),i.preventDefault());break;default:n=!0,this._searchTimeout(i)}},keypress:function(r){if(t){t=!1,r.preventDefault();return}if(n)return;var i=e.ui.keyCode;switch(r.keyCode){case i.PAGE_UP:this._move("previousPage",r);break;case i.PAGE_DOWN:this._move("nextPage",r);break;case i.UP:this._keyEvent("previous",r);break;case i.DOWN:this._keyEvent("next",r)}},input:function(e){if(r){r=!1,e.preventDefault();return}this._searchTimeout(e)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){if(this.cancelBlur){delete this.cancelBlur;return}clearTimeout(this.searching),this.close(e),this._change(e)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete").appendTo(this.document.find(this.options.appendTo||"body")[0]).menu({input:e(),role:null}).zIndex(this.element.zIndex()+1).hide().data("menu"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var n=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(r){r.target!==t.element[0]&&r.target!==n&&!e.contains(n,r.target)&&t.close()})})},menufocus:function(t,n){if(this.isNewMenu){this.isNewMenu=!1;if(t.originalEvent&&/^mouse/.test(t.originalEvent.type)){this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)});return}}var r=n.item.data("ui-autocomplete-item")||n.item.data("item.autocomplete");!1!==this._trigger("focus",t,{item:r})?t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(r.value):this.liveRegion.text(r.value)},menuselect:function(e,t){var n=t.item.data("ui-autocomplete-item")||t.item.data("item.autocomplete"),r=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=r,this._delay(function(){this.previous=r,this.selectedItem=n})),!1!==this._trigger("select",e,{item:n})&&this._value(n.value),this.term=this._value(),this.close(e),this.selectedItem=n}}),this.liveRegion=e("<span>",{role:"status","aria-live":"polite"}).addClass("ui-helper-hidden-accessible").insertAfter(this.element),e.fn.bgiframe&&this.menu.element.bgiframe(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),e==="source"&&this._initSource(),e==="appendTo"&&this.menu.element.appendTo(this.document.find(t||"body")[0]),e==="disabled"&&t&&this.xhr&&this.xhr.abort()},_isMultiLine:function(){return this.element.is("textarea")?!0:this.element.is("input")?!1:this.element.prop("isContentEditable")},_initSource:function(){var t,n,r=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(n,r){r(e.ui.autocomplete.filter(t,n.term))}):typeof this.options.source=="string"?(n=this.options.source,this.source=function(t,i){r.xhr&&r.xhr.abort(),r.xhr=e.ajax({url:n,data:t,dataType:"json",success:function(e){i(e)},error:function(){i([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){this.term!==this._value()&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){e=e!=null?e:this._value(),this.term=this._value();if(e.length<this.options.minLength)return this.close(t);if(this._trigger("search",t)===!1)return;return this._search(e)},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var e=this,t=++n;return function(r){t===n&&e.__response(r),e.pending--,e.pending||e.element.removeClass("ui-autocomplete-loading")}},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return typeof t=="string"?{label:t,value:t}:e.extend({label:t.label||t.value,value:t.value||t.label},t)})},_suggest:function(t){var n=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(n,t),this.menu.refresh(),n.show(),this._resizeMenu(),n.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,n){var r=this;e.each(n,function(e,n){r._renderItemData(t,n)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,n){return e("<li>").append(e("<a>").text(n.label)).appendTo(t)},_move:function(e,t){if(!this.menu.element.is(":visible")){this.search(null,t);return}if(this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)){this._value(this.term),this.menu.blur();return}this.menu[e](t)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){if(!this.isMultiLine||this.menu.element.is(":visible"))this._move(e,t),t.preventDefault()}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,n){var r=new RegExp(e.ui.autocomplete.escapeRegex(n),"i");return e.grep(t,function(e){return r.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var t;this._superApply(arguments);if(this.options.disabled||this.cancelSearch)return;e&&e.length?t=this.options.messages.results(e.length):t=this.options.messages.noResults,this.liveRegion.text(t)}})})(jQuery);(function(e,t){var n,r,i,s,o="ui-button ui-widget ui-state-default ui-corner-all",u="ui-state-hover ui-state-active ",a="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",f=function(){var t=e(this).find(":ui-button");setTimeout(function(){t.button("refresh")},1)},l=function(t){var n=t.name,r=t.form,i=e([]);return n&&(r?i=e(r).find("[name='"+n+"']"):i=e("[name='"+n+"']",t.ownerDocument).filter(function(){return!this.form})),i};e.widget("ui.button",{version:"1.9.1",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,f),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var t=this,u=this.options,a=this.type==="checkbox"||this.type==="radio",c="ui-state-hover"+(a?"":" ui-state-active"),h="ui-state-focus";u.label===null&&(u.label=this.type==="input"?this.buttonElement.val():this.buttonElement.html()),this.buttonElement.addClass(o).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){if(u.disabled)return;e(this).addClass("ui-state-hover"),this===n&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){if(u.disabled)return;e(this).removeClass(c)}).bind("click"+this.eventNamespace,function(e){u.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this.element.bind("focus"+this.eventNamespace,function(){t.buttonElement.addClass(h)}).bind("blur"+this.eventNamespace,function(){t.buttonElement.removeClass(h)}),a&&(this.element.bind("change"+this.eventNamespace,function(){if(s)return;t.refresh()}),this.buttonElement.bind("mousedown"+this.eventNamespace,function(e){if(u.disabled)return;s=!1,r=e.pageX,i=e.pageY}).bind("mouseup"+this.eventNamespace,function(e){if(u.disabled)return;if(r!==e.pageX||i!==e.pageY)s=!0})),this.type==="checkbox"?this.buttonElement.bind("click"+this.eventNamespace,function(){if(u.disabled||s)return!1;e(this).toggleClass("ui-state-active"),t.buttonElement.attr("aria-pressed",t.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click"+this.eventNamespace,function(){if(u.disabled||s)return!1;e(this).addClass("ui-state-active"),t.buttonElement.attr("aria-pressed","true");var n=t.element[0];l(n).not(n).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){if(u.disabled)return!1;e(this).addClass("ui-state-active"),n=this,t.document.one("mouseup",function(){n=null})}).bind("mouseup"+this.eventNamespace,function(){if(u.disabled)return!1;e(this).removeClass("ui-state-active")}).bind("keydown"+this.eventNamespace,function(t){if(u.disabled)return!1;(t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active")}).bind("keyup"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",u.disabled),this._resetButton()},_determineButtonType:function(){var e,t,n;this.element.is("[type=checkbox]")?this.type="checkbox":this.element.is("[type=radio]")?this.type="radio":this.element.is("input")?this.type="input":this.type="button",this.type==="checkbox"||this.type==="radio"?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),n=this.element.is(":checked"),n&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",n)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(o+" "+u+" "+a).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){this._super(e,t);if(e==="disabled"){t?this.element.prop("disabled",!0):this.element.prop("disabled",!1);return}this._resetButton()},refresh:function(){var t=this.element.is(":disabled")||this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),this.type==="radio"?l(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input"){this.options.label&&this.element.val(this.options.label);return}var t=this.buttonElement.removeClass(a),n=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),r=this.options.icons,i=r.primary&&r.secondary,s=[];r.primary||r.secondary?(this.options.text&&s.push("ui-button-text-icon"+(i?"s":r.primary?"-primary":"-secondary")),r.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+r.primary+"'></span>"),r.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+r.secondary+"'></span>"),this.options.text||(s.push(i?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(n)))):s.push("ui-button-text-only"),t.addClass(s.join(" "))}}),e.widget("ui.buttonset",{version:"1.9.1",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){e==="disabled"&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}})})(jQuery);(function($,undefined){function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function bindHover(e){var t="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.delegate(t,"mouseout",function(){$(this).removeClass("ui-state-hover"),this.className.indexOf("ui-datepicker-prev")!=-1&&$(this).removeClass("ui-datepicker-prev-hover"),this.className.indexOf("ui-datepicker-next")!=-1&&$(this).removeClass("ui-datepicker-next-hover")}).delegate(t,"mouseover",function(){$.datepicker._isDisabledDatepicker(instActive.inline?e.parent()[0]:instActive.input[0])||($(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),$(this).addClass("ui-state-hover"),this.className.indexOf("ui-datepicker-prev")!=-1&&$(this).addClass("ui-datepicker-prev-hover"),this.className.indexOf("ui-datepicker-next")!=-1&&$(this).addClass("ui-datepicker-next-hover"))})}function extendRemove(e,t){$.extend(e,t);for(var n in t)if(t[n]==null||t[n]==undefined)e[n]=t[n];return e}$.extend($.ui,{datepicker:{version:"1.9.1"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return extendRemove(this._defaults,e||{}),this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(e,t){var n=e[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:n,input:e,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:t,dpDiv:t?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(e,t){var n=$(e);t.append=$([]),t.trigger=$([]);if(n.hasClass(this.markerClassName))return;this._attachments(n,t),n.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,n,r){t.settings[n]=r}).bind("getData.datepicker",function(e,n){return this._get(t,n)}),this._autoSize(t),$.data(e,PROP_NAME,t),t.settings.disabled&&this._disableDatepicker(e)},_attachments:function(e,t){var n=this._get(t,"appendText"),r=this._get(t,"isRTL");t.append&&t.append.remove(),n&&(t.append=$('<span class="'+this._appendClass+'">'+n+"</span>"),e[r?"before":"after"](t.append)),e.unbind("focus",this._showDatepicker),t.trigger&&t.trigger.remove();var i=this._get(t,"showOn");(i=="focus"||i=="both")&&e.focus(this._showDatepicker);if(i=="button"||i=="both"){var s=this._get(t,"buttonText"),o=this._get(t,"buttonImage");t.trigger=$(this._get(t,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:o,alt:s,title:s}):$('<button type="button"></button>').addClass(this._triggerClass).html(o==""?s:$("<img/>").attr({src:o,alt:s,title:s}))),e[r?"before":"after"](t.trigger),t.trigger.click(function(){return $.datepicker._datepickerShowing&&$.datepicker._lastInput==e[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=e[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(e[0])):$.datepicker._showDatepicker(e[0]),!1})}},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t=new Date(2009,11,20),n=this._get(e,"dateFormat");if(n.match(/[DM]/)){var r=function(e){var t=0,n=0;for(var r=0;r<e.length;r++)e[r].length>t&&(t=e[r].length,n=r);return n};t.setMonth(r(this._get(e,n.match(/MM/)?"monthNames":"monthNamesShort"))),t.setDate(r(this._get(e,n.match(/DD/)?"dayNames":"dayNamesShort"))+20-t.getDay())}e.input.attr("size",this._formatDate(e,t).length)}},_inlineDatepicker:function(e,t){var n=$(e);if(n.hasClass(this.markerClassName))return;n.addClass(this.markerClassName).append(t.dpDiv).bind("setData.datepicker",function(e,n,r){t.settings[n]=r}).bind("getData.datepicker",function(e,n){return this._get(t,n)}),$.data(e,PROP_NAME,t),this._setDate(t,this._getDefaultDate(t),!0),this._updateDatepicker(t),this._updateAlternate(t),t.settings.disabled&&this._disableDatepicker(e),t.dpDiv.css("display","block")},_dialogDatepicker:function(e,t,n,r,i){var s=this._dialogInst;if(!s){this.uuid+=1;var o="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+o+'" style="position: absolute; top: -100px; width: 0px;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),s=this._dialogInst=this._newInst(this._dialogInput,!1),s.settings={},$.data(this._dialogInput[0],PROP_NAME,s)}extendRemove(s.settings,r||{}),t=t&&t.constructor==Date?this._formatDate(s,t):t,this._dialogInput.val(t),this._pos=i?i.length?i:[i.pageX,i.pageY]:null;if(!this._pos){var u=document.documentElement.clientWidth,a=document.documentElement.clientHeight,f=document.documentElement.scrollLeft||document.body.scrollLeft,l=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[u/2-100+f,a/2-150+l]}return this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),s.settings.onSelect=n,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,s),this},_destroyDatepicker:function(e){var t=$(e),n=$.data(e,PROP_NAME);if(!t.hasClass(this.markerClassName))return;var r=e.nodeName.toLowerCase();$.removeData(e,PROP_NAME),r=="input"?(n.append.remove(),n.trigger.remove(),t.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(r=="div"||r=="span")&&t.removeClass(this.markerClassName).empty()},_enableDatepicker:function(e){var t=$(e),n=$.data(e,PROP_NAME);if(!t.hasClass(this.markerClassName))return;var r=e.nodeName.toLowerCase();if(r=="input")e.disabled=!1,n.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(r=="div"||r=="span"){var i=t.children("."+this._inlineClass);i.children().removeClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)}this._disabledInputs=$.map(this._disabledInputs,function(t){return t==e?null:t})},_disableDatepicker:function(e){var t=$(e),n=$.data(e,PROP_NAME);if(!t.hasClass(this.markerClassName))return;var r=e.nodeName.toLowerCase();if(r=="input")e.disabled=!0,n.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(r=="div"||r=="span"){var i=t.children("."+this._inlineClass);i.children().addClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)}this._disabledInputs=$.map(this._disabledInputs,function(t){return t==e?null:t}),this._disabledInputs[this._disabledInputs.length]=e},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;t<this._disabledInputs.length;t++)if(this._disabledInputs[t]==e)return!0;return!1},_getInst:function(e){try{return $.data(e,PROP_NAME)}catch(t){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(e,t,n){var r=this._getInst(e);if(arguments.length==2&&typeof t=="string")return t=="defaults"?$.extend({},$.datepicker._defaults):r?t=="all"?$.extend({},r.settings):this._get(r,t):null;var i=t||{};typeof t=="string"&&(i={},i[t]=n);if(r){this._curInst==r&&this._hideDatepicker();var s=this._getDateDatepicker(e,!0),o=this._getMinMaxDate(r,"min"),u=this._getMinMaxDate(r,"max");extendRemove(r.settings,i),o!==null&&i.dateFormat!==undefined&&i.minDate===undefined&&(r.settings.minDate=this._formatDate(r,o)),u!==null&&i.dateFormat!==undefined&&i.maxDate===undefined&&(r.settings.maxDate=this._formatDate(r,u)),this._attachments($(e),r),this._autoSize(r),this._setDate(r,s),this._updateAlternate(r),this._updateDatepicker(r)}},_changeDatepicker:function(e,t,n){this._optionDatepicker(e,t,n)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var n=this._getInst(e);n&&(this._setDate(n,t),this._updateDatepicker(n),this._updateAlternate(n))},_getDateDatepicker:function(e,t){var n=this._getInst(e);return n&&!n.inline&&this._setDateFromField(n,t),n?this._getDate(n):null},_doKeyDown:function(e){var t=$.datepicker._getInst(e.target),n=!0,r=t.dpDiv.is(".ui-datepicker-rtl");t._keyEvent=!0;if($.datepicker._datepickerShowing)switch(e.keyCode){case 9:$.datepicker._hideDatepicker(),n=!1;break;case 13:var i=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",t.dpDiv);i[0]&&$.datepicker._selectDay(e.target,t.selectedMonth,t.selectedYear,i[0]);var s=$.datepicker._get(t,"onSelect");if(s){var o=$.datepicker._formatDate(t);s.apply(t.input?t.input[0]:null,[o,t])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(e.target,e.ctrlKey?-$.datepicker._get(t,"stepBigMonths"):-$.datepicker._get(t,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(e.target,e.ctrlKey?+$.datepicker._get(t,"stepBigMonths"):+$.datepicker._get(t,"stepMonths"),"M");break;case 35:(e.ctrlKey||e.metaKey)&&$.datepicker._clearDate(e.target),n=e.ctrlKey||e.metaKey;break;case 36:(e.ctrlKey||e.metaKey)&&$.datepicker._gotoToday(e.target),n=e.ctrlKey||e.metaKey;break;case 37:(e.ctrlKey||e.metaKey)&&$.datepicker._adjustDate(e.target,r?1:-1,"D"),n=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&$.datepicker._adjustDate(e.target,e.ctrlKey?-$.datepicker._get(t,"stepBigMonths"):-$.datepicker._get(t,"stepMonths"),"M");break;case 38:(e.ctrlKey||e.metaKey)&&$.datepicker._adjustDate(e.target,-7,"D"),n=e.ctrlKey||e.metaKey;break;case 39:(e.ctrlKey||e.metaKey)&&$.datepicker._adjustDate(e.target,r?-1:1,"D"),n=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&$.datepicker._adjustDate(e.target,e.ctrlKey?+$.datepicker._get(t,"stepBigMonths"):+$.datepicker._get(t,"stepMonths"),"M");break;case 40:(e.ctrlKey||e.metaKey)&&$.datepicker._adjustDate(e.target,7,"D"),n=e.ctrlKey||e.metaKey;break;default:n=!1}else e.keyCode==36&&e.ctrlKey?$.datepicker._showDatepicker(this):n=!1;n&&(e.preventDefault(),e.stopPropagation())},_doKeyPress:function(e){var t=$.datepicker._getInst(e.target);if($.datepicker._get(t,"constrainInput")){var n=$.datepicker._possibleChars($.datepicker._get(t,"dateFormat")),r=String.fromCharCode(e.charCode==undefined?e.keyCode:e.charCode);return e.ctrlKey||e.metaKey||r<" "||!n||n.indexOf(r)>-1}},_doKeyUp:function(e){var t=$.datepicker._getInst(e.target);if(t.input.val()!=t.lastVal)try{var n=$.datepicker.parseDate($.datepicker._get(t,"dateFormat"),t.input?t.input.val():null,$.datepicker._getFormatConfig(t));n&&($.datepicker._setDateFromField(t),$.datepicker._updateAlternate(t),$.datepicker._updateDatepicker(t))}catch(r){$.datepicker.log(r)}return!0},_showDatepicker:function(e){e=e.target||e,e.nodeName.toLowerCase()!="input"&&(e=$("input",e.parentNode)[0]);if($.datepicker._isDisabledDatepicker(e)||$.datepicker._lastInput==e)return;var t=$.datepicker._getInst(e);$.datepicker._curInst&&$.datepicker._curInst!=t&&($.datepicker._curInst.dpDiv.stop(!0,!0),t&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var n=$.datepicker._get(t,"beforeShow"),r=n?n.apply(e,[e,t]):{};if(r===!1)return;extendRemove(t.settings,r),t.lastVal=null,$.datepicker._lastInput=e,$.datepicker._setDateFromField(t),$.datepicker._inDialog&&(e.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(e),$.datepicker._pos[1]+=e.offsetHeight);var i=!1;$(e).parents().each(function(){return i|=$(this).css("position")=="fixed",!i});var s={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,t.dpDiv.empty(),t.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(t),s=$.datepicker._checkOffset(t,s,i),t.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":i?"fixed":"absolute",display:"none",left:s.left+"px",top:s.top+"px"});if(!t.inline){var o=$.datepicker._get(t,"showAnim"),u=$.datepicker._get(t,"duration"),a=function(){var e=t.dpDiv.find("iframe.ui-datepicker-cover");if(!!e.length){var n=$.datepicker._getBorders(t.dpDiv);e.css({left:-n[0],top:-n[1],width:t.dpDiv.outerWidth(),height:t.dpDiv.outerHeight()})}};t.dpDiv.zIndex($(e).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&($.effects.effect[o]||$.effects[o])?t.dpDiv.show(o,$.datepicker._get(t,"showOptions"),u,a):t.dpDiv[o||"show"](o?u:null,a),(!o||!u)&&a(),t.input.is(":visible")&&!t.input.is(":disabled")&&t.input.focus(),$.datepicker._curInst=t}},_updateDatepicker:function(e){this.maxRows=4;var t=$.datepicker._getBorders(e.dpDiv);instActive=e,e.dpDiv.empty().append(this._generateHTML(e)),this._attachHandlers(e);var n=e.dpDiv.find("iframe.ui-datepicker-cover");!n.length||n.css({left:-t[0],top:-t[1],width:e.dpDiv.outerWidth(),height:e.dpDiv.outerHeight()}),e.dpDiv.find("."+this._dayOverClass+" a").mouseover();var r=this._getNumberOfMonths(e),i=r[1],s=17;e.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),i>1&&e.dpDiv.addClass("ui-datepicker-multi-"+i).css("width",s*i+"em"),e.dpDiv[(r[0]!=1||r[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),e.dpDiv[(this._get(e,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),e==$.datepicker._curInst&&$.datepicker._datepickerShowing&&e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&e.input[0]!=document.activeElement&&e.input.focus();if(e.yearshtml){var o=e.yearshtml;setTimeout(function(){o===e.yearshtml&&e.yearshtml&&e.dpDiv.find("select.ui-datepicker-year:first").replaceWith(e.yearshtml),o=e.yearshtml=null},0)}},_getBorders:function(e){var t=function(e){return{thin:1,medium:2,thick:3}[e]||e};return[parseFloat(t(e.css("border-left-width"))),parseFloat(t(e.css("border-top-width")))]},_checkOffset:function(e,t,n){var r=e.dpDiv.outerWidth(),i=e.dpDiv.outerHeight(),s=e.input?e.input.outerWidth():0,o=e.input?e.input.outerHeight():0,u=document.documentElement.clientWidth+(n?0:$(document).scrollLeft()),a=document.documentElement.clientHeight+(n?0:$(document).scrollTop());return t.left-=this._get(e,"isRTL")?r-s:0,t.left-=n&&t.left==e.input.offset().left?$(document).scrollLeft():0,t.top-=n&&t.top==e.input.offset().top+o?$(document).scrollTop():0,t.left-=Math.min(t.left,t.left+r>u&&u>r?Math.abs(t.left+r-u):0),t.top-=Math.min(t.top,t.top+i>a&&a>i?Math.abs(i+o):0),t},_findPos:function(e){var t=this._getInst(e),n=this._get(t,"isRTL");while(e&&(e.type=="hidden"||e.nodeType!=1||$.expr.filters.hidden(e)))e=e[n?"previousSibling":"nextSibling"];var r=$(e).offset();return[r.left,r.top]},_hideDatepicker:function(e){var t=this._curInst;if(!t||e&&t!=$.data(e,PROP_NAME))return;if(this._datepickerShowing){var n=this._get(t,"showAnim"),r=this._get(t,"duration"),i=function(){$.datepicker._tidyDialog(t)};$.effects&&($.effects.effect[n]||$.effects[n])?t.dpDiv.hide(n,$.datepicker._get(t,"showOptions"),r,i):t.dpDiv[n=="slideDown"?"slideUp":n=="fadeIn"?"fadeOut":"hide"](n?r:null,i),n||i(),this._datepickerShowing=!1;var s=this._get(t,"onClose");s&&s.apply(t.input?t.input[0]:null,[t.input?t.input.val():"",t]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(e){if(!$.datepicker._curInst)return;var t=$(e.target),n=$.datepicker._getInst(t[0]);(t[0].id!=$.datepicker._mainDivId&&t.parents("#"+$.datepicker._mainDivId).length==0&&!t.hasClass($.datepicker.markerClassName)&&!t.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||t.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=n)&&$.datepicker._hideDatepicker()},_adjustDate:function(e,t,n){var r=$(e),i=this._getInst(r[0]);if(this._isDisabledDatepicker(r[0]))return;this._adjustInstDate(i,t+(n=="M"?this._get(i,"showCurrentAtPos"):0),n),this._updateDatepicker(i)},_gotoToday:function(e){var t=$(e),n=this._getInst(t[0]);if(this._get(n,"gotoCurrent")&&n.currentDay)n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear;else{var r=new Date;n.selectedDay=r.getDate(),n.drawMonth=n.selectedMonth=r.getMonth(),n.drawYear=n.selectedYear=r.getFullYear()}this._notifyChange(n),this._adjustDate(t)},_selectMonthYear:function(e,t,n){var r=$(e),i=this._getInst(r[0]);i["selected"+(n=="M"?"Month":"Year")]=i["draw"+(n=="M"?"Month":"Year")]=parseInt(t.options[t.selectedIndex].value,10),this._notifyChange(i),this._adjustDate(r)},_selectDay:function(e,t,n,r){var i=$(e);if($(r).hasClass(this._unselectableClass)||this._isDisabledDatepicker(i[0]))return;var s=this._getInst(i[0]);s.selectedDay=s.currentDay=$("a",r).html(),s.selectedMonth=s.currentMonth=t,s.selectedYear=s.currentYear=n,this._selectDate(e,this._formatDate(s,s.currentDay,s.currentMonth,s.currentYear))},_clearDate:function(e){var t=$(e),n=this._getInst(t[0]);this._selectDate(t,"")},_selectDate:function(e,t){var n=$(e),r=this._getInst(n[0]);t=t!=null?t:this._formatDate(r),r.input&&r.input.val(t),this._updateAlternate(r);var i=this._get(r,"onSelect");i?i.apply(r.input?r.input[0]:null,[t,r]):r.input&&r.input.trigger("change"),r.inline?this._updateDatepicker(r):(this._hideDatepicker(),this._lastInput=r.input[0],typeof r.input[0]!="object"&&r.input.focus(),this._lastInput=null)},_updateAlternate:function(e){var t=this._get(e,"altField");if(t){var n=this._get(e,"altFormat")||this._get(e,"dateFormat"),r=this._getDate(e),i=this.formatDate(n,r,this._getFormatConfig(e));$(t).each(function(){$(this).val(i)})}},noWeekends:function(e){var t=e.getDay();return[t>0&&t<6,""]},iso8601Week:function(e){var t=new Date(e.getTime());t.setDate(t.getDate()+4-(t.getDay()||7));var n=t.getTime();return t.setMonth(0),t.setDate(1),Math.floor(Math.round((n-t)/864e5)/7)+1},parseDate:function(e,t,n){if(e==null||t==null)throw"Invalid arguments";t=typeof t=="object"?t.toString():t+"";if(t=="")return null;var r=(n?n.shortYearCutoff:null)||this._defaults.shortYearCutoff;r=typeof r!="string"?r:(new Date).getFullYear()%100+parseInt(r,10);var i=(n?n.dayNamesShort:null)||this._defaults.dayNamesShort,s=(n?n.dayNames:null)||this._defaults.dayNames,o=(n?n.monthNamesShort:null)||this._defaults.monthNamesShort,u=(n?n.monthNames:null)||this._defaults.monthNames,a=-1,f=-1,l=-1,c=-1,h=!1,p=function(t){var n=y+1<e.length&&e.charAt(y+1)==t;return n&&y++,n},d=function(e){var n=p(e),r=e=="@"?14:e=="!"?20:e=="y"&&n?4:e=="o"?3:2,i=new RegExp("^\\d{1,"+r+"}"),s=t.substring(g).match(i);if(!s)throw"Missing number at position "+g;return g+=s[0].length,parseInt(s[0],10)},v=function(e,n,r){var i=$.map(p(e)?r:n,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)}),s=-1;$.each(i,function(e,n){var r=n[1];if(t.substr(g,r.length).toLowerCase()==r.toLowerCase())return s=n[0],g+=r.length,!1});if(s!=-1)return s+1;throw"Unknown name at position "+g},m=function(){if(t.charAt(g)!=e.charAt(y))throw"Unexpected literal at position "+g;g++},g=0;for(var y=0;y<e.length;y++)if(h)e.charAt(y)=="'"&&!p("'")?h=!1:m();else switch(e.charAt(y)){case"d":l=d("d");break;case"D":v("D",i,s);break;case"o":c=d("o");break;case"m":f=d("m");break;case"M":f=v("M",o,u);break;case"y":a=d("y");break;case"@":var b=new Date(d("@"));a=b.getFullYear(),f=b.getMonth()+1,l=b.getDate();break;case"!":var b=new Date((d("!")-this._ticksTo1970)/1e4);a=b.getFullYear(),f=b.getMonth()+1,l=b.getDate();break;case"'":p("'")?m():h=!0;break;default:m()}if(g<t.length){var w=t.substr(g);if(!/^\s+/.test(w))throw"Extra/unparsed characters found in date: "+w}a==-1?a=(new Date).getFullYear():a<100&&(a+=(new Date).getFullYear()-(new Date).getFullYear()%100+(a<=r?0:-100));if(c>-1){f=1,l=c;do{var E=this._getDaysInMonth(a,f-1);if(l<=E)break;f++,l-=E}while(!0)}var b=this._daylightSavingAdjust(new Date(a,f-1,l));if(b.getFullYear()!=a||b.getMonth()+1!=f||b.getDate()!=l)throw"Invalid date";return b},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(e,t,n){if(!t)return"";var r=(n?n.dayNamesShort:null)||this._defaults.dayNamesShort,i=(n?n.dayNames:null)||this._defaults.dayNames,s=(n?n.monthNamesShort:null)||this._defaults.monthNamesShort,o=(n?n.monthNames:null)||this._defaults.monthNames,u=function(t){var n=h+1<e.length&&e.charAt(h+1)==t;return n&&h++,n},a=function(e,t,n){var r=""+t;if(u(e))while(r.length<n)r="0"+r;return r},f=function(e,t,n,r){return u(e)?r[t]:n[t]},l="",c=!1;if(t)for(var h=0;h<e.length;h++)if(c)e.charAt(h)=="'"&&!u("'")?c=!1:l+=e.charAt(h);else switch(e.charAt(h)){case"d":l+=a("d",t.getDate(),2);break;case"D":l+=f("D",t.getDay(),r,i);break;case"o":l+=a("o",Math.round(((new Date(t.getFullYear(),t.getMonth(),t.getDate())).getTime()-(new Date(t.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":l+=a("m",t.getMonth()+1,2);break;case"M":l+=f("M",t.getMonth(),s,o);break;case"y":l+=u("y")?t.getFullYear():(t.getYear()%100<10?"0":"")+t.getYear()%100;break;case"@":l+=t.getTime();break;case"!":l+=t.getTime()*1e4+this._ticksTo1970;break;case"'":u("'")?l+="'":c=!0;break;default:l+=e.charAt(h)}return l},_possibleChars:function(e){var t="",n=!1,r=function(t){var n=i+1<e.length&&e.charAt(i+1)==t;return n&&i++,n};for(var i=0;i<e.length;i++)if(n)e.charAt(i)=="'"&&!r("'")?n=!1:t+=e.charAt(i);else switch(e.charAt(i)){case"d":case"m":case"y":case"@":t+="0123456789";break;case"D":case"M":return null;case"'":r("'")?t+="'":n=!0;break;default:t+=e.charAt(i)}return t},_get:function(e,t){return e.settings[t]!==undefined?e.settings[t]:this._defaults[t]},_setDateFromField:function(e,t){if(e.input.val()==e.lastVal)return;var n=this._get(e,"dateFormat"),r=e.lastVal=e.input?e.input.val():null,i,s;i=s=this._getDefaultDate(e);var o=this._getFormatConfig(e);try{i=this.parseDate(n,r,o)||s}catch(u){this.log(u),r=t?"":r}e.selectedDay=i.getDate(),e.drawMonth=e.selectedMonth=i.getMonth(),e.drawYear=e.selectedYear=i.getFullYear(),e.currentDay=r?i.getDate():0,e.currentMonth=r?i.getMonth():0,e.currentYear=r?i.getFullYear():0,this._adjustInstDate(e)},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(e,t,n){var r=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},i=function(t){try{return $.datepicker.parseDate($.datepicker._get(e,"dateFormat"),t,$.datepicker._getFormatConfig(e))}catch(n){}var r=(t.toLowerCase().match(/^c/)?$.datepicker._getDate(e):null)||new Date,i=r.getFullYear(),s=r.getMonth(),o=r.getDate(),u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,a=u.exec(t);while(a){switch(a[2]||"d"){case"d":case"D":o+=parseInt(a[1],10);break;case"w":case"W":o+=parseInt(a[1],10)*7;break;case"m":case"M":s+=parseInt(a[1],10),o=Math.min(o,$.datepicker._getDaysInMonth(i,s));break;case"y":case"Y":i+=parseInt(a[1],10),o=Math.min(o,$.datepicker._getDaysInMonth(i,s))}a=u.exec(t)}return new Date(i,s,o)},s=t==null||t===""?n:typeof t=="string"?i(t):typeof t=="number"?isNaN(t)?n:r(t):new Date(t.getTime());return s=s&&s.toString()=="Invalid Date"?n:s,s&&(s.setHours(0),s.setMinutes(0),s.setSeconds(0),s.setMilliseconds(0)),this._daylightSavingAdjust(s)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,n){var r=!t,i=e.selectedMonth,s=e.selectedYear,o=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=o.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=o.getMonth(),e.drawYear=e.selectedYear=e.currentYear=o.getFullYear(),(i!=e.selectedMonth||s!=e.selectedYear)&&!n&&this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(r?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&e.input.val()==""?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(e){var t=this._get(e,"stepMonths"),n="#"+e.id.replace(/\\\\/g,"\\");e.dpDiv.find("[data-handler]").map(function(){var e={prev:function(){window["DP_jQuery_"+dpuuid].datepicker._adjustDate(n,-t,"M")},next:function(){window["DP_jQuery_"+dpuuid].datepicker._adjustDate(n,+t,"M")},hide:function(){window["DP_jQuery_"+dpuuid].datepicker._hideDatepicker()},today:function(){window["DP_jQuery_"+dpuuid].datepicker._gotoToday(n)},selectDay:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectDay(n,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectMonthYear(n,this,"M"),!1},selectYear:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectMonthYear(n,this,"Y"),!1}};$(this).bind(this.getAttribute("data-event"),e[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t=new Date;t=this._daylightSavingAdjust(new Date(t.getFullYear(),t.getMonth(),t.getDate()));var n=this._get(e,"isRTL"),r=this._get(e,"showButtonPanel"),i=this._get(e,"hideIfNoPrevNext"),s=this._get(e,"navigationAsDateFormat"),o=this._getNumberOfMonths(e),u=this._get(e,"showCurrentAtPos"),a=this._get(e,"stepMonths"),f=o[0]!=1||o[1]!=1,l=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),c=this._getMinMaxDate(e,"min"),h=this._getMinMaxDate(e,"max"),p=e.drawMonth-u,d=e.drawYear;p<0&&(p+=12,d--);if(h){var v=this._daylightSavingAdjust(new Date(h.getFullYear(),h.getMonth()-o[0]*o[1]+1,h.getDate()));v=c&&v<c?c:v;while(this._daylightSavingAdjust(new Date(d,p,1))>v)p--,p<0&&(p=11,d--)}e.drawMonth=p,e.drawYear=d;var m=this._get(e,"prevText");m=s?this.formatDate(m,this._daylightSavingAdjust(new Date(d,p-a,1)),this._getFormatConfig(e)):m;var g=this._canAdjustMonth(e,-1,d,p)?'<a class="ui-datepicker-prev ui-corner-all" data-handler="prev" data-event="click" title="'+m+'"><span class="ui-icon ui-icon-circle-triangle-'+(n?"e":"w")+'">'+m+"</span></a>":i?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+m+'"><span class="ui-icon ui-icon-circle-triangle-'+(n?"e":"w")+'">'+m+"</span></a>",y=this._get(e,"nextText");y=s?this.formatDate(y,this._daylightSavingAdjust(new Date(d,p+a,1)),this._getFormatConfig(e)):y;var b=this._canAdjustMonth(e,1,d,p)?'<a class="ui-datepicker-next ui-corner-all" data-handler="next" data-event="click" title="'+y+'"><span class="ui-icon ui-icon-circle-triangle-'+(n?"w":"e")+'">'+y+"</span></a>":i?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+y+'"><span class="ui-icon ui-icon-circle-triangle-'+(n?"w":"e")+'">'+y+"</span></a>",w=this._get(e,"currentText"),E=this._get(e,"gotoCurrent")&&e.currentDay?l:t;w=s?this.formatDate(w,E,this._getFormatConfig(e)):w;var S=e.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">'+this._get(e,"closeText")+"</button>",x=r?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(n?S:"")+(this._isInRange(e,E)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" data-handler="today" data-event="click">'+w+"</button>":"")+(n?"":S)+"</div>":"",T=parseInt(this._get(e,"firstDay"),10);T=isNaN(T)?0:T;var N=this._get(e,"showWeek"),C=this._get(e,"dayNames"),k=this._get(e,"dayNamesShort"),L=this._get(e,"dayNamesMin"),A=this._get(e,"monthNames"),O=this._get(e,"monthNamesShort"),M=this._get(e,"beforeShowDay"),_=this._get(e,"showOtherMonths"),D=this._get(e,"selectOtherMonths"),P=this._get(e,"calculateWeek")||this.iso8601Week,H=this._getDefaultDate(e),B="";for(var j=0;j<o[0];j++){var F="";this.maxRows=4;for(var I=0;I<o[1];I++){var q=this._daylightSavingAdjust(new Date(d,p,e.selectedDay)),R=" ui-corner-all",U="";if(f){U+='<div class="ui-datepicker-group';if(o[1]>1)switch(I){case 0:U+=" ui-datepicker-group-first",R=" ui-corner-"+(n?"right":"left");break;case o[1]-1:U+=" ui-datepicker-group-last",R=" ui-corner-"+(n?"left":"right");break;default:U+=" ui-datepicker-group-middle",R=""}U+='">'}U+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+R+'">'+(/all|left/.test(R)&&j==0?n?b:g:"")+(/all|right/.test(R)&&j==0?n?g:b:"")+this._generateMonthYearHeader(e,p,d,c,h,j>0||I>0,A,O)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var z=N?'<th class="ui-datepicker-week-col">'+this._get(e,"weekHeader")+"</th>":"";for(var W=0;W<7;W++){var X=(W+T)%7;z+="<th"+((W+T+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+C[X]+'">'+L[X]+"</span></th>"}U+=z+"</tr></thead><tbody>";var V=this._getDaysInMonth(d,p);d==e.selectedYear&&p==e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,V));var J=(this._getFirstDayOfMonth(d,p)-T+7)%7,K=Math.ceil((J+V)/7),Q=f?this.maxRows>K?this.maxRows:K:K;this.maxRows=Q;var G=this._daylightSavingAdjust(new Date(d,p,1-J));for(var Y=0;Y<Q;Y++){U+="<tr>";var Z=N?'<td class="ui-datepicker-week-col">'+this._get(e,"calculateWeek")(G)+"</td>":"";for(var W=0;W<7;W++){var et=M?M.apply(e.input?e.input[0]:null,[G]):[!0,""],tt=G.getMonth()!=p,nt=tt&&!D||!et[0]||c&&G<c||h&&G>h;Z+='<td class="'+((W+T+6)%7>=5?" ui-datepicker-week-end":"")+(tt?" ui-datepicker-other-month":"")+(G.getTime()==q.getTime()&&p==e.selectedMonth&&e._keyEvent||H.getTime()==G.getTime()&&H.getTime()==q.getTime()?" "+this._dayOverClass:"")+(nt?" "+this._unselectableClass+" ui-state-disabled":"")+(tt&&!_?"":" "+et[1]+(G.getTime()==l.getTime()?" "+this._currentClass:"")+(G.getTime()==t.getTime()?" ui-datepicker-today":""))+'"'+((!tt||_)&&et[2]?' title="'+et[2]+'"':"")+(nt?"":' data-handler="selectDay" data-event="click" data-month="'+G.getMonth()+'" data-year="'+G.getFullYear()+'"')+">"+(tt&&!_?" ":nt?'<span class="ui-state-default">'+G.getDate()+"</span>":'<a class="ui-state-default'+(G.getTime()==t.getTime()?" ui-state-highlight":"")+(G.getTime()==l.getTime()?" ui-state-active":"")+(tt?" ui-priority-secondary":"")+'" href="#">'+G.getDate()+"</a>")+"</td>",G.setDate(G.getDate()+1),G=this._daylightSavingAdjust(G)}U+=Z+"</tr>"}p++,p>11&&(p=0,d++),U+="</tbody></table>"+(f?"</div>"+(o[0]>0&&I==o[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),F+=U}B+=F}return B+=x+($.ui.ie6&&!e.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),e._keyEvent=!1,B},_generateMonthYearHeader:function(e,t,n,r,i,s,o,u){var a=this._get(e,"changeMonth"),f=this._get(e,"changeYear"),l=this._get(e,"showMonthAfterYear"),c='<div class="ui-datepicker-title">',h="";if(s||!a)h+='<span class="ui-datepicker-month">'+o[t]+"</span>";else{var p=r&&r.getFullYear()==n,d=i&&i.getFullYear()==n;h+='<select class="ui-datepicker-month" data-handler="selectMonth" data-event="change">';for(var v=0;v<12;v++)(!p||v>=r.getMonth())&&(!d||v<=i.getMonth())&&(h+='<option value="'+v+'"'+(v==t?' selected="selected"':"")+">"+u[v]+"</option>");h+="</select>"}l||(c+=h+(s||!a||!f?" ":""));if(!e.yearshtml){e.yearshtml="";if(s||!f)c+='<span class="ui-datepicker-year">'+n+"</span>";else{var m=this._get(e,"yearRange").split(":"),g=(new Date).getFullYear(),y=function(e){var t=e.match(/c[+-].*/)?n+parseInt(e.substring(1),10):e.match(/[+-].*/)?g+parseInt(e,10):parseInt(e,10);return isNaN(t)?g:t},b=y(m[0]),w=Math.max(b,y(m[1]||""));b=r?Math.max(b,r.getFullYear()):b,w=i?Math.min(w,i.getFullYear()):w,e.yearshtml+='<select class="ui-datepicker-year" data-handler="selectYear" data-event="change">';for(;b<=w;b++)e.yearshtml+='<option value="'+b+'"'+(b==n?' selected="selected"':"")+">"+b+"</option>";e.yearshtml+="</select>",c+=e.yearshtml,e.yearshtml=null}}return c+=this._get(e,"yearSuffix"),l&&(c+=(s||!a||!f?" ":"")+h),c+="</div>",c},_adjustInstDate:function(e,t,n){var r=e.drawYear+(n=="Y"?t:0),i=e.drawMonth+(n=="M"?t:0),s=Math.min(e.selectedDay,this._getDaysInMonth(r,i))+(n=="D"?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(r,i,s)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),(n=="M"||n=="Y")&&this._notifyChange(e)},_restrictMinMax:function(e,t){var n=this._getMinMaxDate(e,"min"),r=this._getMinMaxDate(e,"max"),i=n&&t<n?n:t;return i=r&&i>r?r:i,i},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return t==null?[1,1]:typeof t=="number"?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return(new Date(e,t,1)).getDay()},_canAdjustMonth:function(e,t,n,r){var i=this._getNumberOfMonths(e),s=this._daylightSavingAdjust(new Date(n,r+(t<0?t:i[0]*i[1]),1));return t<0&&s.setDate(this._getDaysInMonth(s.getFullYear(),s.getMonth())),this._isInRange(e,s)},_isInRange:function(e,t){var n=this._getMinMaxDate(e,"min"),r=this._getMinMaxDate(e,"max");return(!n||t.getTime()>=n.getTime())&&(!r||t.getTime()<=r.getTime())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t=typeof t!="string"?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,n,r){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var i=t?typeof t=="object"?t:this._daylightSavingAdjust(new Date(r,n,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),i,this._getFormatConfig(e))}}),$.fn.datepicker=function(e){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find(document.body).append($.datepicker.dpDiv),$.datepicker.initialized=!0);var t=Array.prototype.slice.call(arguments,1);return typeof e!="string"||e!="isDisabled"&&e!="getDate"&&e!="widget"?e=="option"&&arguments.length==2&&typeof arguments[1]=="string"?$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this[0]].concat(t)):this.each(function(){typeof e=="string"?$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this].concat(t)):$.datepicker._attachDatepicker(this,e)}):$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this[0]].concat(t))},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.9.1",window["DP_jQuery_"+dpuuid]=$})(jQuery);(function(e,t){var n="ui-dialog ui-widget ui-widget-content ui-corner-all ",r={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},i={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0};e.widget("ui.dialog",{version:"1.9.1",options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var n=e(this).css(t).offset().top;n<0&&e(this).css("top",t.top-n)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.oldPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.options.title=this.options.title||this.originalTitle;var t=this,r=this.options,i=r.title||" ",s,o,u,a,f;s=(this.uiDialog=e("<div>")).addClass(n+r.dialogClass).css({display:"none",outline:0,zIndex:r.zIndex}).attr("tabIndex",-1).keydown(function(n){r.closeOnEscape&&!n.isDefaultPrevented()&&n.keyCode&&n.keyCode===e.ui.keyCode.ESCAPE&&(t.close(n),n.preventDefault())}).mousedown(function(e){t.moveToTop(!1,e)}).appendTo("body"),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(s),o=(this.uiDialogTitlebar=e("<div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").bind("mousedown",function(){s.focus()}).prependTo(s),u=e("<a href='#'></a>").addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").click(function(e){e.preventDefault(),t.close(e)}).appendTo(o),(this.uiDialogTitlebarCloseText=e("<span>")).addClass("ui-icon ui-icon-closethick").text(r.closeText).appendTo(u),a=e("<span>").uniqueId().addClass("ui-dialog-title").html(i).prependTo(o),f=(this.uiDialogButtonPane=e("<div>")).addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),(this.uiButtonSet=e("<div>")).addClass("ui-dialog-buttonset").appendTo(f),s.attr({role:"dialog","aria-labelledby":a.attr("id")}),o.find("*").add(o).disableSelection(),this._hoverable(u),this._focusable(u),r.draggable&&e.fn.draggable&&this._makeDraggable(),r.resizable&&e.fn.resizable&&this._makeResizable(),this._createButtons(r.buttons),this._isOpen=!1,e.fn.bgiframe&&s.bgiframe(),this._on(s,{keydown:function(t){if(!r.modal||t.keyCode!==e.ui.keyCode.TAB)return;var n=e(":tabbable",s),i=n.filter(":first"),o=n.filter(":last");if(t.target===o[0]&&!t.shiftKey)return i.focus(1),!1;if(t.target===i[0]&&t.shiftKey)return o.focus(1),!1}})},_init:function(){this.options.autoOpen&&this.open()},_destroy:function(){var e,t=this.oldPosition;this.overlay&&this.overlay.destroy(),this.uiDialog.hide(),this.element.removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),this.uiDialog.remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},close:function(t){var n=this,r,i;if(!this._isOpen)return;if(!1===this._trigger("beforeClose",t))return;return this._isOpen=!1,this.overlay&&this.overlay.destroy(),this.options.hide?this._hide(this.uiDialog,this.options.hide,function(){n._trigger("close",t)}):(this.uiDialog.hide(),this._trigger("close",t)),e.ui.dialog.overlay.resize(),this.options.modal&&(r=0,e(".ui-dialog").each(function(){this!==n.uiDialog[0]&&(i=e(this).css("z-index"),isNaN(i)||(r=Math.max(r,i)))}),e.ui.dialog.maxZ=r),this},isOpen:function(){return this._isOpen},moveToTop:function(t,n){var r=this.options,i;return r.modal&&!t||!r.stack&&!r.modal?this._trigger("focus",n):(r.zIndex>e.ui.dialog.maxZ&&(e.ui.dialog.maxZ=r.zIndex),this.overlay&&(e.ui.dialog.maxZ+=1,e.ui.dialog.overlay.maxZ=e.ui.dialog.maxZ,this.overlay.$el.css("z-index",e.ui.dialog.overlay.maxZ)),i={scrollTop:this.element.scrollTop(),scrollLeft:this.element.scrollLeft()},e.ui.dialog.maxZ+=1,this.uiDialog.css("z-index",e.ui.dialog.maxZ),this.element.attr(i),this._trigger("focus",n),this)},open:function(){if(this._isOpen)return;var t,n=this.options,r=this.uiDialog;return this._size(),this._position(n.position),r.show(n.show),this.overlay=n.modal?new e.ui.dialog.overlay(this):null,this.moveToTop(!0),t=this.element.find(":tabbable"),t.length||(t=this.uiDialogButtonPane.find(":tabbable"),t.length||(t=r)),t.eq(0).focus(),this._isOpen=!0,this._trigger("open"),this},_createButtons:function(t){var n=this,r=!1;this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),typeof t=="object"&&t!==null&&e.each(t,function(){return!(r=!0)}),r?(e.each(t,function(t,r){r=e.isFunction(r)?{click:r,text:t}:r;var i=e("<button type='button'></button>").attr(r,!0).unbind("click").click(function(){r.click.apply(n.element[0],arguments)}).appendTo(n.uiButtonSet);e.fn.button&&i.button()}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog)):this.uiDialog.removeClass("ui-dialog-buttons")},_makeDraggable:function(){function r(e){return{position:e.position,offset:e.offset}}var t=this,n=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(n,i){e(this).addClass("ui-dialog-dragging"),t._trigger("dragStart",n,r(i))},drag:function(e,n){t._trigger("drag",e,r(n))},stop:function(i,s){n.position=[s.position.left-t.document.scrollLeft(),s.position.top-t.document.scrollTop()],e(this).removeClass("ui-dialog-dragging"),t._trigger("dragStop",i,r(s)),e.ui.dialog.overlay.resize()}})},_makeResizable:function(n){function u(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}n=n===t?this.options.resizable:n;var r=this,i=this.options,s=this.uiDialog.css("position"),o=typeof n=="string"?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:i.maxWidth,maxHeight:i.maxHeight,minWidth:i.minWidth,minHeight:this._minHeight(),handles:o,start:function(t,n){e(this).addClass("ui-dialog-resizing"),r._trigger("resizeStart",t,u(n))},resize:function(e,t){r._trigger("resize",e,u(t))},stop:function(t,n){e(this).removeClass("ui-dialog-resizing"),i.height=e(this).height(),i.width=e(this).width(),r._trigger("resizeStop",t,u(n)),e.ui.dialog.overlay.resize()}}).css("position",s).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var e=this.options;return e.height==="auto"?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(t){var n=[],r=[0,0],i;if(t){if(typeof t=="string"||typeof t=="object"&&"0"in t)n=t.split?t.split(" "):[t[0],t[1]],n.length===1&&(n[1]=n[0]),e.each(["left","top"],function(e,t){+n[e]===n[e]&&(r[e]=n[e],n[e]=t)}),t={my:n[0]+(r[0]<0?r[0]:"+"+r[0])+" "+n[1]+(r[1]<0?r[1]:"+"+r[1]),at:n.join(" ")};t=e.extend({},e.ui.dialog.prototype.options.position,t)}else t=e.ui.dialog.prototype.options.position;i=this.uiDialog.is(":visible"),i||this.uiDialog.show(),this.uiDialog.position(t),i||this.uiDialog.hide()},_setOptions:function(t){var n=this,s={},o=!1;e.each(t,function(e,t){n._setOption(e,t),e in r&&(o=!0),e in i&&(s[e]=t)}),o&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",s)},_setOption:function(t,r){var i,s,o=this.uiDialog;switch(t){case"buttons":this._createButtons(r);break;case"closeText":this.uiDialogTitlebarCloseText.text(""+r);break;case"dialogClass":o.removeClass(this.options.dialogClass).addClass(n+r);break;case"disabled":r?o.addClass("ui-dialog-disabled"):o.removeClass("ui-dialog-disabled");break;case"draggable":i=o.is(":data(draggable)"),i&&!r&&o.draggable("destroy"),!i&&r&&this._makeDraggable();break;case"position":this._position(r);break;case"resizable":s=o.is(":data(resizable)"),s&&!r&&o.resizable("destroy"),s&&typeof r=="string"&&o.resizable("option","handles",r),!s&&r!==!1&&this._makeResizable(r);break;case"title":e(".ui-dialog-title",this.uiDialogTitlebar).html(""+(r||" "))}this._super(t,r)},_size:function(){var t,n,r,i=this.options,s=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),i.minWidth>i.width&&(i.width=i.minWidth),t=this.uiDialog.css({height:"auto",width:i.width}).outerHeight(),n=Math.max(0,i.minHeight-t),i.height==="auto"?e.support.minHeight?this.element.css({minHeight:n,height:"auto"}):(this.uiDialog.show(),r=this.element.css("height","auto").height(),s||this.uiDialog.hide(),this.element.height(Math.max(r,n))):this.element.height(Math.max(i.height-t,0)),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),e.extend(e.ui.dialog,{uuid:0,maxZ:0,getTitleId:function(e){var t=e.attr("id");return t||(this.uuid+=1,t=this.uuid),"ui-dialog-title-"+t},overlay:function(t){this.$el=e.ui.dialog.overlay.create(t)}}),e.extend(e.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:e.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(e){return e+".dialog-overlay"}).join(" "),create:function(t){this.instances.length===0&&(setTimeout(function(){e.ui.dialog.overlay.instances.length&&e(document).bind(e.ui.dialog.overlay.events,function(t){if(e(t.target).zIndex()<e.ui.dialog.overlay.maxZ)return!1})},1),e(window).bind("resize.dialog-overlay",e.ui.dialog.overlay.resize));var n=this.oldInstances.pop()||e("<div>").addClass("ui-widget-overlay");return e(document).bind("keydown.dialog-overlay",function(r){var i=e.ui.dialog.overlay.instances;i.length!==0&&i[i.length-1]===n&&t.options.closeOnEscape&&!r.isDefaultPrevented()&&r.keyCode&&r.keyCode===e.ui.keyCode.ESCAPE&&(t.close(r),r.preventDefault())}),n.appendTo(document.body).css({width:this.width(),height:this.height()}),e.fn.bgiframe&&n.bgiframe(),this.instances.push(n),n},destroy:function(t){var n=e.inArray(t,this.instances),r=0;n!==-1&&this.oldInstances.push(this.instances.splice(n,1)[0]),this.instances.length===0&&e([document,window]).unbind(".dialog-overlay"),t.height(0).width(0).remove(),e.each(this.instances,function(){r=Math.max(r,this.css("z-index"))}),this.maxZ=r},height:function(){var t,n;return e.ui.ie?(t=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),n=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight),t<n?e(window).height()+"px":t+"px"):e(document).height()+"px"},width:function(){var t,n;return e.ui.ie?(t=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),n=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth),t<n?e(window).width()+"px":t+"px"):e(document).width()+"px"},resize:function(){var t=e([]);e.each(e.ui.dialog.overlay.instances,function(){t=t.add(this)}),t.css({width:0,height:0}).css({width:e.ui.dialog.overlay.width(),height:e.ui.dialog.overlay.height()})}}),e.extend(e.ui.dialog.overlay.prototype,{destroy:function(){e.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);(function(e,t){var n=!1;e.widget("ui.menu",{version:"1.9.1",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,e.proxy(function(e){this.options.disabled&&e.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(e){e.preventDefault()},"click .ui-state-disabled > a":function(e){e.preventDefault()},"click .ui-menu-item:has(a)":function(t){var r=e(t.target).closest(".ui-menu-item");!n&&r.not(".ui-state-disabled").length&&(n=!0,this.select(t),r.has(".ui-menu").length?this.expand(t):this.element.is(":focus")||(this.element.trigger("focus",[!0]),this.active&&this.active.parents(".ui-menu").length===1&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){var n=e(t.currentTarget);n.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(t,n)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var n=this.active||this.element.children(".ui-menu-item").eq(0);t||this.focus(e,n)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){e(t.target).closest(".ui-menu").length||this.collapseAll(t),n=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").andSelf().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){function a(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var n,r,i,s,o,u=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:u=!1,r=this.previousFilter||"",i=String.fromCharCode(t.keyCode),s=!1,clearTimeout(this.filterTimer),i===r?s=!0:i=r+i,o=new RegExp("^"+a(i),"i"),n=this.activeMenu.children(".ui-menu-item").filter(function(){return o.test(e(this).children("a").text())}),n=s&&n.index(this.active.next())!==-1?this.active.nextAll(".ui-menu-item"):n,n.length||(i=String.fromCharCode(t.keyCode),o=new RegExp("^"+a(i),"i"),n=this.activeMenu.children(".ui-menu-item").filter(function(){return o.test(e(this).children("a").text())})),n.length?(this.focus(t,n),n.length>1?(this.previousFilter=i,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}u&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(e):this.select(e))},refresh:function(){var t,n=this.options.icons.submenu,r=this.element.find(this.options.menus+":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"});t=r.add(this.element),t.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),t.children(":not(.ui-menu-item)").each(function(){var t=e(this);/[^\-—–\s]/.test(t.text())||t.addClass("ui-widget-content ui-menu-divider")}),t.children(".ui-state-disabled").attr("aria-disabled","true"),r.each(function(){var t=e(this),r=t.prev("a"),i=e("<span>").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);r.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",r.attr("id"))}),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},focus:function(e,t){var n,r;this.blur(e,e&&e.type==="focus"),this._scrollIntoView(t),this.active=t.first(),r=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",r.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),e&&e.type==="keydown"?this._close():this.timer=this._delay(function(){this._close()},this.delay),n=t.children(".ui-menu"),n.length&&/^mouse/.test(e.type)&&this._startOpening(n),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var n,r,i,s,o,u;this._hasScroll()&&(n=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,r=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,i=t.offset().top-this.activeMenu.offset().top-n-r,s=this.activeMenu.scrollTop(),o=this.activeMenu.height(),u=t.height(),i<0?this.activeMenu.scrollTop(s+i):i+u>o&&this.activeMenu.scrollTop(s+i-o+u))},blur:function(e,t){t||clearTimeout(this.timer);if(!this.active)return;this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active})},_startOpening:function(e){clearTimeout(this.timer);if(e.attr("aria-hidden")!=="true")return;this.timer=this._delay(function(){this._close(),this._open(e)},this.delay)},_open:function(t){var n=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(n)},collapseAll:function(t,n){clearTimeout(this.timer),this.timer=this._delay(function(){var r=n?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));r.length||(r=this.element),this._close(r),this.blur(t),this.activeMenu=r},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,n){var r;this.active&&(e==="first"||e==="last"?r=this.active[e==="first"?"prevAll":"nextAll"](".ui-menu-item").eq(-1):r=this.active[e+"All"](".ui-menu-item").eq(0));if(!r||!r.length||!this.active)r=this.activeMenu.children(".ui-menu-item")[t]();this.focus(n,r)},nextPage:function(t){var n,r,i;if(!this.active){this.next(t);return}if(this.isLastItem())return;this._hasScroll()?(r=this.active.offset().top,i=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return n=e(this),n.offset().top-r-i<0}),this.focus(t,n)):this.focus(t,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())},previousPage:function(t){var n,r,i;if(!this.active){this.next(t);return}if(this.isFirstItem())return;this._hasScroll()?(r=this.active.offset().top,i=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return n=e(this),n.offset().top-r+i>0}),this.focus(t,n)):this.focus(t,this.activeMenu.children(".ui-menu-item").first())},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||e(t.target).closest(".ui-menu-item");var n={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,n)}})})(jQuery);(function(e,t){e.widget("ui.progressbar",{version:"1.9.1",options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=e("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return e===t?this._value():(this._setOption("value",e),this)},_setOption:function(e,t){e==="value"&&(this.options.value=t,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),this._super(e,t)},_value:function(){var e=this.options.value;return typeof e!="number"&&(e=0),Math.min(this.options.max,Math.max(this.min,e))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var e=this.value(),t=this._percentage();this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),this.valueDiv.toggle(e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(t.toFixed(0)+"%"),this.element.attr("aria-valuenow",e)}})})(jQuery);(function(e,t){var n=5;e.widget("ui.slider",e.ui.mouse,{version:"1.9.1",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var t,r,i=this.options,s=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),o="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",u=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(i.disabled?" ui-slider-disabled ui-disabled":"")),this.range=e([]),i.range&&(i.range===!0&&(i.values||(i.values=[this._valueMin(),this._valueMin()]),i.values.length&&i.values.length!==2&&(i.values=[i.values[0],i.values[0]])),this.range=e("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(i.range==="min"||i.range==="max"?" ui-slider-range-"+i.range:""))),r=i.values&&i.values.length||1;for(t=s.length;t<r;t++)u.push(o);this.handles=s.add(e(u.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(e){e.preventDefault()}).mouseenter(function(){i.disabled||e(this).addClass("ui-state-hover")}).mouseleave(function(){e(this).removeClass("ui-state-hover")}).focus(function(){i.disabled?e(this).blur():(e(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),e(this).addClass("ui-state-focus"))}).blur(function(){e(this).removeClass("ui-state-focus")}),this.handles.each(function(t){e(this).data("ui-slider-handle-index",t)}),this._on(this.handles,{keydown:function(t){var r,i,s,o,u=e(t.target).data("ui-slider-handle-index");switch(t.keyCode){case e.ui.keyCode.HOME:case e.ui.keyCode.END:case e.ui.keyCode.PAGE_UP:case e.ui.keyCode.PAGE_DOWN:case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:t.preventDefault();if(!this._keySliding){this._keySliding=!0,e(t.target).addClass("ui-state-active"),r=this._start(t,u);if(r===!1)return}}o=this.options.step,this.options.values&&this.options.values.length?i=s=this.values(u):i=s=this.value();switch(t.keyCode){case e.ui.keyCode.HOME:s=this._valueMin();break;case e.ui.keyCode.END:s=this._valueMax();break;case e.ui.keyCode.PAGE_UP:s=this._trimAlignValue(i+(this._valueMax()-this._valueMin())/n);break;case e.ui.keyCode.PAGE_DOWN:s=this._trimAlignValue(i-(this._valueMax()-this._valueMin())/n);break;case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:if(i===this._valueMax())return;s=this._trimAlignValue(i+o);break;case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(i===this._valueMin())return;s=this._trimAlignValue(i-o)}this._slide(t,u,s)},keyup:function(t){var n=e(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,n),this._change(t,n),e(t.target).removeClass("ui-state-active"))}}),this._refreshValue(),this._animateOff=!1},_destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(t){var n,r,i,s,o,u,a,f,l=this,c=this.options;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),n={x:t.pageX,y:t.pageY},r=this._normValueFromMouse(n),i=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var n=Math.abs(r-l.values(t));i>n&&(i=n,s=e(this),o=t)}),c.range===!0&&this.values(1)===c.min&&(o+=1,s=e(this.handles[o])),u=this._start(t,o),u===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,s.addClass("ui-state-active").focus(),a=s.offset(),f=!e(t.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=f?{left:0,top:0}:{left:t.pageX-a.left-s.width()/2,top:t.pageY-a.top-s.height()/2-(parseInt(s.css("borderTopWidth"),10)||0)-(parseInt(s.css("borderBottomWidth"),10)||0)+(parseInt(s.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,r),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},n=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,n),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,n,r,i,s;return this.orientation==="horizontal"?(t=this.elementSize.width,n=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,n=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),r=n/t,r>1&&(r=1),r<0&&(r=0),this.orientation==="vertical"&&(r=1-r),i=this._valueMax()-this._valueMin(),s=this._valueMin()+r*i,this._trimAlignValue(s)},_start:function(e,t){var n={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(n.value=this.values(t),n.values=this.values()),this._trigger("start",e,n)},_slide:function(e,t,n){var r,i,s;this.options.values&&this.options.values.length?(r=this.values(t?0:1),this.options.values.length===2&&this.options.range===!0&&(t===0&&n>r||t===1&&n<r)&&(n=r),n!==this.values(t)&&(i=this.values(),i[t]=n,s=this._trigger("slide",e,{handle:this.handles[t],value:n,values:i}),r=this.values(t?0:1),s!==!1&&this.values(t,n,!0))):n!==this.value()&&(s=this._trigger("slide",e,{handle:this.handles[t],value:n}),s!==!1&&this.value(n))},_stop:function(e,t){var n={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(n.value=this.values(t),n.values=this.values()),this._trigger("stop",e,n)},_change:function(e,t){if(!this._keySliding&&!this._mouseSliding){var n={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(n.value=this.values(t),n.values=this.values()),this._trigger("change",e,n)}},value:function(e){if(arguments.length){this.options.value=this._trimAlignValue(e),this._refreshValue(),this._change(null,0);return}return this._value()},values:function(t,n){var r,i,s;if(arguments.length>1){this.options.values[t]=this._trimAlignValue(n),this._refreshValue(),this._change(null,t);return}if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();r=this.options.values,i=arguments[0];for(s=0;s<r.length;s+=1)r[s]=this._trimAlignValue(i[s]),this._change(null,s);this._refreshValue()},_setOption:function(t,n){var r,i=0;e.isArray(this.options.values)&&(i=this.options.values.length),e.Widget.prototype._setOption.apply(this,arguments);switch(t){case"disabled":n?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.prop("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.prop("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(r=0;r<i;r+=1)this._change(null,r);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1}},_value:function(){var e=this.options.value;return e=this._trimAlignValue(e),e},_values:function(e){var t,n,r;if(arguments.length)return t=this.options.values[e],t=this._trimAlignValue(t),t;n=this.options.values.slice();for(r=0;r<n.length;r+=1)n[r]=this._trimAlignValue(n[r]);return n},_trimAlignValue:function(e){if(e<=this._valueMin())return this._valueMin();if(e>=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,n=(e-this._valueMin())%t,r=e-n;return Math.abs(n)*2>=t&&(r+=n>0?t:-t),parseFloat(r.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var t,n,r,i,s,o=this.options.range,u=this.options,a=this,f=this._animateOff?!1:u.animate,l={};this.options.values&&this.options.values.length?this.handles.each(function(r){n=(a.values(r)-a._valueMin())/(a._valueMax()-a._valueMin())*100,l[a.orientation==="horizontal"?"left":"bottom"]=n+"%",e(this).stop(1,1)[f?"animate":"css"](l,u.animate),a.options.range===!0&&(a.orientation==="horizontal"?(r===0&&a.range.stop(1,1)[f?"animate":"css"]({left:n+"%"},u.animate),r===1&&a.range[f?"animate":"css"]({width:n-t+"%"},{queue:!1,duration:u.animate})):(r===0&&a.range.stop(1,1)[f?"animate":"css"]({bottom:n+"%"},u.animate),r===1&&a.range[f?"animate":"css"]({height:n-t+"%"},{queue:!1,duration:u.animate}))),t=n}):(r=this.value(),i=this._valueMin(),s=this._valueMax(),n=s!==i?(r-i)/(s-i)*100:0,l[this.orientation==="horizontal"?"left":"bottom"]=n+"%",this.handle.stop(1,1)[f?"animate":"css"](l,u.animate),o==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[f?"animate":"css"]({width:n+"%"},u.animate),o==="max"&&this.orientation==="horizontal"&&this.range[f?"animate":"css"]({width:100-n+"%"},{queue:!1,duration:u.animate}),o==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[f?"animate":"css"]({height:n+"%"},u.animate),o==="max"&&this.orientation==="vertical"&&this.range[f?"animate":"css"]({height:100-n+"%"},{queue:!1,duration:u.animate}))}})})(jQuery);(function(e){function t(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.widget("ui.spinner",{version:"1.9.1",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},n=this.element;return e.each(["min","max","step"],function(e,r){var i=n.attr(r);i!==undefined&&i.length&&(t[r]=i)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){if(this.cancelBlur){delete this.cancelBlur;return}this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e)},mousewheel:function(e,t){if(!t)return;if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()},"mousedown .ui-spinner-button":function(t){function r(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=n,this._delay(function(){this.previous=n}))}var n;n=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),r.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,r.call(this)});if(this._start(t)===!1)return;this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){if(!e(t.currentTarget).hasClass("ui-state-active"))return;if(this._start(t)===!1)return!1;this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(e.height()*.5)&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var n=this.options,r=e.ui.keyCode;switch(t.keyCode){case r.UP:return this._repeat(null,1,t),!0;case r.DOWN:return this._repeat(null,-1,t),!0;case r.PAGE_UP:return this._repeat(null,n.page,t),!0;case r.PAGE_DOWN:return this._repeat(null,-n.page,t),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>▲</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>▼</span>"+"</a>"},_start:function(e){return!this.spinning&&this._trigger("start",e)===!1?!1:(this.counter||(this.counter=1),this.spinning=!0,!0)},_repeat:function(e,t,n){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,n)},e),this._spin(t*this.options.step,n)},_spin:function(e,t){var n=this.value()||0;this.counter||(this.counter=1),n=this._adjustValue(n+e*this._increment(this.counter));if(!this.spinning||this._trigger("spin",t,{value:n})!==!1)this._value(n),this.counter++},_increment:function(t){var n=this.options.incremental;return n?e.isFunction(n)?n(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return this.options.min!==null&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=e.toString(),n=t.indexOf(".");return n===-1?0:t.length-n-1},_adjustValue:function(e){var t,n,r=this.options;return t=r.min!==null?r.min:0,n=e-t,n=Math.round(n/r.step)*r.step,e=t+n,e=parseFloat(e.toFixed(this._precision())),r.max!==null&&e>r.max?r.max:r.min!==null&&e<r.min?r.min:e},_stop:function(e){if(!this.spinning)return;clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e)},_setOption:function(e,t){if(e==="culture"||e==="numberFormat"){var n=this._parse(this.element.val());this.options[e]=t,this.element.val(this._format(n));return}(e==="max"||e==="min"||e==="step")&&typeof t=="string"&&(t=this._parse(t)),this._super(e,t),e==="disabled"&&(t?(this.element.prop("disabled",!0),this.buttons.button("disable")):(this.element.prop("disabled",!1),this.buttons.button("enable")))},_setOptions:t(function(e){this._super(e),this._value(this.element.val())}),_parse:function(e){return typeof e=="string"&&e!==""&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),e===""||isNaN(e)?null:e},_format:function(e){return e===""?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},_value:function(e,t){var n;e!==""&&(n=this._parse(e),n!==null&&(t||(n=this._adjustValue(n)),e=this._format(n))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:t(function(e){this._stepUp(e)}),_stepUp:function(e){this._spin((e||1)*this.options.step)},stepDown:t(function(e){this._stepDown(e)}),_stepDown:function(e){this._spin((e||1)*-this.options.step)},pageUp:t(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:t(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){if(!arguments.length)return this._parse(this.element.val());t(this._value).call(this,e)},widget:function(){return this.uiSpinner}})})(jQuery);(function(e,t){function i(){return++n}function s(e){return e.hash.length>1&&e.href.replace(r,"")===location.href.replace(r,"")}var n=0,r=/#.*$/;e.widget("ui.tabs",{version:"1.9.1",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var t=this,n=this.options,r=n.active,i=location.hash.substring(1);this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",n.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs();if(r===null){i&&this.tabs.each(function(t,n){if(e(n).attr("aria-controls")===i)return r=t,!1}),r===null&&(r=this.tabs.index(this.tabs.filter(".ui-tabs-active")));if(r===null||r===-1)r=this.tabs.length?0:!1}r!==!1&&(r=this.tabs.index(this.tabs.eq(r)),r===-1&&(r=n.collapsible?!1:0)),n.active=r,!n.collapsible&&n.active===!1&&this.anchors.length&&(n.active=0),e.isArray(n.disabled)&&(n.disabled=e.unique(n.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.options.active!==!1&&this.anchors.length?this.active=this._findActive(this.options.active):this.active=e(),this._refresh(),this.active.length&&this.load(n.active)},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var n=e(this.document[0].activeElement).closest("li"),r=this.tabs.index(n),i=!0;if(this._handlePageNav(t))return;switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:r++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:i=!1,r--;break;case e.ui.keyCode.END:r=this.anchors.length-1;break;case e.ui.keyCode.HOME:r=0;break;case e.ui.keyCode.SPACE:t.preventDefault(),clearTimeout(this.activating),this._activate(r);return;case e.ui.keyCode.ENTER:t.preventDefault(),clearTimeout(this.activating),this._activate(r===this.options.active?!1:r);return;default:return}t.preventDefault(),clearTimeout(this.activating),r=this._focusNextTab(r,i),t.ctrlKey||(n.attr("aria-selected","false"),this.tabs.eq(r).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",r)},this.delay))},_panelKeydown:function(t){if(this._handlePageNav(t))return;t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){if(t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP)return this._activate(this._focusNextTab(this.options.active-1,!1)),!0;if(t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN)return this._activate(this._focusNextTab(this.options.active+1,!0)),!0},_findNextTab:function(t,n){function i(){return t>r&&(t=0),t<0&&(t=r),t}var r=this.tabs.length-1;while(e.inArray(i(),this.options.disabled)!==-1)t=n?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){if(e==="active"){this._activate(t);return}if(e==="disabled"){this._setupDisabled(t);return}this._super(e,t),e==="collapsible"&&(this.element.toggleClass("ui-tabs-collapsible",t),!t&&this.options.active===!1&&this._activate(0)),e==="event"&&this._setupEvents(t),e==="heightStyle"&&this._setupHeightStyle(t)},_tabId:function(e){return e.attr("aria-controls")||"ui-tabs-"+i()},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,n=this.tablist.children(":has(a[href])");t.disabled=e.map(n.filter(".ui-state-disabled"),function(e){return n.index(e)}),this._processTabs(),t.active===!1||!this.anchors.length?(t.active=!1,this.active=e()):this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(n,r){var i,o,u,a=e(r).uniqueId().attr("id"),f=e(r).closest("li"),l=f.attr("aria-controls");s(r)?(i=r.hash,o=t.element.find(t._sanitizeSelector(i))):(u=t._tabId(f),i="#"+u,o=t.element.find(i),o.length||(o=t._createPanel(u),o.insertAfter(t.panels[n-1]||t.tablist)),o.attr("aria-live","polite")),o.length&&(t.panels=t.panels.add(o)),l&&f.data("ui-tabs-aria-controls",l),f.attr({"aria-controls":i.substring(1),"aria-labelledby":a}),o.attr("aria-labelledby",a)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("<div>").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var n=0,r;r=this.tabs[n];n++)t===!0||e.inArray(n,t)!==-1?e(r).addClass("ui-state-disabled").attr("aria-disabled","true"):e(r).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var n={click:function(e){e.preventDefault()}};t&&e.each(t.split(" "),function(e,t){n[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,n),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var n,r,i=this.element.parent();t==="fill"?(e.support.minHeight||(r=i.css("overflow"),i.css("overflow","hidden")),n=i.height(),this.element.siblings(":visible").each(function(){var t=e(this),r=t.css("position");if(r==="absolute"||r==="fixed")return;n-=t.outerHeight(!0)}),r&&i.css("overflow",r),this.element.children().not(this.panels).each(function(){n-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,n-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):t==="auto"&&(n=0,this.panels.each(function(){n=Math.max(n,e(this).height("").height())}).height(n))},_eventHandler:function(t){var n=this.options,r=this.active,i=e(t.currentTarget),s=i.closest("li"),o=s[0]===r[0],u=o&&n.collapsible,a=u?e():this._getPanelForTab(s),f=r.length?this._getPanelForTab(r):e(),l={oldTab:r,oldPanel:f,newTab:u?e():s,newPanel:a};t.preventDefault();if(s.hasClass("ui-state-disabled")||s.hasClass("ui-tabs-loading")||this.running||o&&!n.collapsible||this._trigger("beforeActivate",t,l)===!1)return;n.active=u?!1:this.tabs.index(s),this.active=o?e():s,this.xhr&&this.xhr.abort(),!f.length&&!a.length&&e.error("jQuery UI Tabs: Mismatching fragment identifier."),a.length&&this.load(this.tabs.index(s),t),this._toggle(t,l)},_toggle:function(t,n){function o(){r.running=!1,r._trigger("activate",t,n)}function u(){n.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),i.length&&r.options.show?r._show(i,r.options.show,o):(i.show(),o())}var r=this,i=n.newPanel,s=n.oldPanel;this.running=!0,s.length&&this.options.hide?this._hide(s,this.options.hide,function(){n.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),u()}):(n.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),s.hide(),u()),s.attr({"aria-expanded":"false","aria-hidden":"true"}),n.oldTab.attr("aria-selected","false"),i.length&&s.length?n.oldTab.attr("tabIndex",-1):i.length&&this.tabs.filter(function(){return e(this).attr("tabIndex")===0}).attr("tabIndex",-1),i.attr({"aria-expanded":"true","aria-hidden":"false"}),n.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(t){var n,r=this._findActive(t);if(r[0]===this.active[0])return;r.length||(r=this.active),n=r.find(".ui-tabs-anchor")[0],this._eventHandler({target:n,currentTarget:n,preventDefault:e.noop})},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return typeof e=="string"&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeData("href.tabs").removeData("load.tabs").removeUniqueId(),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),n=t.data("ui-tabs-aria-controls");n?t.attr("aria-controls",n):t.removeAttr("aria-controls")}),this.options.heightStyle!=="content"&&this.panels.css("height","")},enable:function(n){var r=this.options.disabled;if(r===!1)return;n===t?r=!1:(n=this._getIndex(n),e.isArray(r)?r=e.map(r,function(e){return e!==n?e:null}):r=e.map(this.tabs,function(e,t){return t!==n?t:null})),this._setupDisabled(r)},disable:function(n){var r=this.options.disabled;if(r===!0)return;if(n===t)r=!0;else{n=this._getIndex(n);if(e.inArray(n,r)!==-1)return;e.isArray(r)?r=e.merge([n],r).sort():r=[n]}this._setupDisabled(r)},load:function(t,n){t=this._getIndex(t);var r=this,i=this.tabs.eq(t),o=i.find(".ui-tabs-anchor"),u=this._getPanelForTab(i),a={tab:i,panel:u};if(s(o[0]))return;this.xhr=e.ajax(this._ajaxSettings(o,n,a)),this.xhr&&this.xhr.statusText!=="canceled"&&(i.addClass("ui-tabs-loading"),u.attr("aria-busy","true"),this.xhr.success(function(e){setTimeout(function(){u.html(e),r._trigger("load",n,a)},1)}).complete(function(e,t){setTimeout(function(){t==="abort"&&r.panels.stop(!1,!0),i.removeClass("ui-tabs-loading"),u.removeAttr("aria-busy"),e===r.xhr&&delete r.xhr},1)}))},_ajaxSettings:function(t,n,r){var i=this;return{url:t.attr("href"),beforeSend:function(t,s){return i._trigger("beforeLoad",n,e.extend({jqXHR:t,ajaxSettings:s},r))}}},_getPanelForTab:function(t){var n=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+n))}}),e.uiBackCompat!==!1&&(e.ui.tabs.prototype._ui=function(e,t){return{tab:e,panel:t,index:this.anchors.index(e)}},e.widget("ui.tabs",e.ui.tabs,{url:function(e,t){this.anchors.eq(e).attr("href",t)}}),e.widget("ui.tabs",e.ui.tabs,{options:{ajaxOptions:null,cache:!1},_create:function(){this._super();var t=this;this._on({tabsbeforeload:function(n,r){if(e.data(r.tab[0],"cache.tabs")){n.preventDefault();return}r.jqXHR.success(function(){t.options.cache&&e.data(r.tab[0],"cache.tabs",!0)})}})},_ajaxSettings:function(t,n,r){var i=this.options.ajaxOptions;return e.extend({},i,{error:function(e,t){try{i.error(e,t,r.tab.closest("li").index(),r.tab[0])}catch(n){}}},this._superApply(arguments))},_setOption:function(e,t){e==="cache"&&t===!1&&this.anchors.removeData("cache.tabs"),this._super(e,t)},_destroy:function(){this.anchors.removeData("cache.tabs"),this._super()},url:function(e){this.anchors.eq(e).removeData("cache.tabs"),this._superApply(arguments)}}),e.widget("ui.tabs",e.ui.tabs,{abort:function(){this.xhr&&this.xhr.abort()}}),e.widget("ui.tabs",e.ui.tabs,{options:{spinner:"<em>Loading…</em>"},_create:function(){this._super(),this._on({tabsbeforeload:function(e,t){if(e.target!==this.element[0]||!this.options.spinner)return;var n=t.tab.find("span"),r=n.html();n.html(this.options.spinner),t.jqXHR.complete(function(){n.html(r)})}})}}),e.widget("ui.tabs",e.ui.tabs,{options:{enable:null,disable:null},enable:function(t){var n=this.options,r;if(t&&n.disabled===!0||e.isArray(n.disabled)&&e.inArray(t,n.disabled)!==-1)r=!0;this._superApply(arguments),r&&this._trigger("enable",null,this._ui(this.anchors[t],this.panels[t]))},disable:function(t){var n=this.options,r;if(t&&n.disabled===!1||e.isArray(n.disabled)&&e.inArray(t,n.disabled)===-1)r=!0;this._superApply(arguments),r&&this._trigger("disable",null,this._ui(this.anchors[t],this.panels[t]))}}),e.widget("ui.tabs",e.ui.tabs,{options:{add:null,remove:null,tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},add:function(n,r,i){i===t&&(i=this.anchors.length);var s,o,u=this.options,a=e(u.tabTemplate.replace(/#\{href\}/g,n).replace(/#\{label\}/g,r)),f=n.indexOf("#")?this._tabId(a):n.replace("#","");return a.addClass("ui-state-default ui-corner-top").data("ui-tabs-destroy",!0),a.attr("aria-controls",f),s=i>=this.tabs.length,o=this.element.find("#"+f),o.length||(o=this._createPanel(f),s?i>0?o.insertAfter(this.panels.eq(-1)):o.appendTo(this.element):o.insertBefore(this.panels[i])),o.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").hide(),s?a.appendTo(this.tablist):a.insertBefore(this.tabs[i]),u.disabled=e.map(u.disabled,function(e){return e>=i?++e:e}),this.refresh(),this.tabs.length===1&&u.active===!1&&this.option("active",0),this._trigger("add",null,this._ui(this.anchors[i],this.panels[i])),this},remove:function(t){t=this._getIndex(t);var n=this.options,r=this.tabs.eq(t).remove(),i=this._getPanelForTab(r).remove();return r.hasClass("ui-tabs-active")&&this.anchors.length>2&&this._activate(t+(t+1<this.anchors.length?1:-1)),n.disabled=e.map(e.grep(n.disabled,function(e){return e!==t}),function(e){return e>=t?--e:e}),this.refresh(),this._trigger("remove",null,this._ui(r.find("a")[0],i[0])),this}}),e.widget("ui.tabs",e.ui.tabs,{length:function(){return this.anchors.length}}),e.widget("ui.tabs",e.ui.tabs,{options:{idPrefix:"ui-tabs-"},_tabId:function(t){var n=t.is("li")?t.find("a[href]"):t;return n=n[0],e(n).closest("li").attr("aria-controls")||n.title&&n.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF\-]/g,"")||this.options.idPrefix+i()}}),e.widget("ui.tabs",e.ui.tabs,{options:{panelTemplate:"<div></div>"},_createPanel:function(t){return e(this.options.panelTemplate).attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)}}),e.widget("ui.tabs",e.ui.tabs,{_create:function(){var e=this.options;e.active===null&&e.selected!==t&&(e.active=e.selected===-1?!1:e.selected),this._super(),e.selected=e.active,e.selected===!1&&(e.selected=-1)},_setOption:function(e,t){if(e!=="selected")return this._super(e,t);var n=this.options;this._super("active",t===-1?!1:t),n.selected=n.active,n.selected===!1&&(n.selected=-1)},_eventHandler:function(){this._superApply(arguments),this.options.selected=this.options.active,this.options.selected===!1&&(this.options.selected=-1)}}),e.widget("ui.tabs",e.ui.tabs,{options:{show:null,select:null},_create:function(){this._super(),this.options.active!==!1&&this._trigger("show",null,this._ui(this.active.find(".ui-tabs-anchor")[0],this._getPanelForTab(this.active)[0]))},_trigger:function(e,t,n){var r=this._superApply(arguments);return r?(e==="beforeActivate"&&n.newTab.length?r=this._super("select",t,{tab:n.newTab.find(".ui-tabs-anchor")[0],panel:n.newPanel[0],index:n.newTab.closest("li").index()}):e==="activate"&&n.newTab.length&&(r=this._super("show",t,{tab:n.newTab.find(".ui-tabs-anchor")[0],panel:n.newPanel[0],index:n.newTab.closest("li").index()})),r):!1}}),e.widget("ui.tabs",e.ui.tabs,{select:function(e){e=this._getIndex(e);if(e===-1){if(!this.options.collapsible||this.options.selected===-1)return;e=this.options.selected}this.anchors.eq(e).trigger(this.options.event+this.eventNamespace)}}),function(){var t=0;e.widget("ui.tabs",e.ui.tabs,{options:{cookie:null},_create:function(){var e=this.options,t;e.active==null&&e.cookie&&(t=parseInt(this._cookie(),10),t===-1&&(t=!1),e.active=t),this._super()},_cookie:function(n){var r=[this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+ ++t)];return arguments.length&&(r.push(n===!1?-1:n),r.push(this.options.cookie)),e.cookie.apply(null,r)},_refresh:function(){this._super(),this.options.cookie&&this._cookie(this.options.active,this.options.cookie)},_eventHandler:function(){this._superApply(arguments),this.options.cookie&&this._cookie(this.options.active,this.options.cookie)},_destroy:function(){this._super(),this.options.cookie&&this._cookie(null,this.options.cookie)}})}(),e.widget("ui.tabs",e.ui.tabs,{_trigger:function(t,n,r){var i=e.extend({},r);return t==="load"&&(i.panel=i.panel[0],i.tab=i.tab.find(".ui-tabs-anchor")[0]),this._super(t,n,i)}}),e.widget("ui.tabs",e.ui.tabs,{options:{fx:null},_getFx:function(){var t,n,r=this.options.fx;return r&&(e.isArray(r)?(t=r[0],n=r[1]):t=n=r),r?{show:n,hide:t}:null},_toggle:function(e,t){function o(){n.running=!1,n._trigger("activate",e,t)}function u(){t.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),r.length&&s.show?r.animate(s.show,s.show.duration,function(){o()}):(r.show(),o())}var n=this,r=t.newPanel,i=t.oldPanel,s=this._getFx();if(!s)return this._super(e,t);n.running=!0,i.length&&s.hide?i.animate(s.hide,s.hide.duration,function(){t.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),u()}):(t.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),i.hide(),u())}}))})(jQuery);(function(e){function n(t,n){var r=(t.attr("aria-describedby")||"").split(/\s+/);r.push(n),t.data("ui-tooltip-id",n).attr("aria-describedby",e.trim(r.join(" ")))}function r(t){var n=t.data("ui-tooltip-id"),r=(t.attr("aria-describedby")||"").split(/\s+/),i=e.inArray(n,r);i!==-1&&r.splice(i,1),t.removeData("ui-tooltip-id"),r=e.trim(r.join(" ")),r?t.attr("aria-describedby",r):t.removeAttr("aria-describedby")}var t=0;e.widget("ui.tooltip",{version:"1.9.1",options:{content:function(){return e(this).attr("title")},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flipfit"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(t,n){var r=this;if(t==="disabled"){this[n?"_disable":"_enable"](),this.options[t]=n;return}this._super(t,n),t==="content"&&e.each(this.tooltips,function(e,t){r._updateContent(t)})},_disable:function(){var t=this;e.each(this.tooltips,function(n,r){var i=e.Event("blur");i.target=i.currentTarget=r[0],t.close(i,!0)}),this.element.find(this.options.items).andSelf().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).andSelf().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var n=this,r=e(t?t.target:this.element).closest(this.options.items);if(!r.length)return;if(this.options.track&&r.data("ui-tooltip-id")){this._find(r).position(e.extend({of:r},this.options.position)),this._off(this.document,"mousemove");return}r.attr("title")&&r.data("ui-tooltip-title",r.attr("title")),r.data("tooltip-open",!0),t&&t.type==="mouseover"&&r.parents().each(function(){var t;e(this).data("tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,n.close(t,!0)),this.title&&(e(this).uniqueId(),n.parents[this.id]={element:this,title:this.title},this.title="")}),this._updateContent(r,t)},_updateContent:function(e,t){var n,r=this.options.content,i=this;if(typeof r=="string")return this._open(t,e,r);n=r.call(e[0],function(n){if(!e.data("tooltip-open"))return;i._delay(function(){this._open(t,e,n)})}),n&&this._open(t,e,n)},_open:function(t,r,i){function f(e){a.of=e;if(s.is(":hidden"))return;s.position(a)}var s,o,u,a=e.extend({},this.options.position);if(!i)return;s=this._find(r);if(s.length){s.find(".ui-tooltip-content").html(i);return}r.is("[title]")&&(t&&t.type==="mouseover"?r.attr("title",""):r.removeAttr("title")),s=this._tooltip(r),n(r,s.attr("id")),s.find(".ui-tooltip-content").html(i),this.options.track&&t&&/^mouse/.test(t.originalEvent.type)?(this._on(this.document,{mousemove:f}),f(t)):s.position(e.extend({of:r},this.options.position)),s.hide(),this._show(s,this.options.show),this.options.show&&this.options.show.delay&&(u=setInterval(function(){s.is(":visible")&&(f(a.of),clearInterval(u))},e.fx.interval)),this._trigger("open",t,{tooltip:s}),o={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var n=e.Event(t);n.currentTarget=r[0],this.close(n,!0)}},remove:function(){this._removeTooltip(s)}};if(!t||t.type==="mouseover")o.mouseleave="close";if(!t||t.type==="focusin")o.focusout="close";this._on(r,o)},close:function(t){var n=this,i=e(t?t.currentTarget:this.element),s=this._find(i);if(this.closing)return;i.data("ui-tooltip-title")&&i.attr("title",i.data("ui-tooltip-title")),r(i),s.stop(!0),this._hide(s,this.options.hide,function(){n._removeTooltip(e(this))}),i.removeData("tooltip-open"),this._off(i,"mouseleave focusout keyup"),i[0]!==this.element[0]&&this._off(i,"remove"),this._off(this.document,"mousemove"),t&&t.type==="mouseleave"&&e.each(this.parents,function(e,t){t.element.title=t.title,delete n.parents[e]}),this.closing=!0,this._trigger("close",t,{tooltip:s}),this.closing=!1},_tooltip:function(n){var r="ui-tooltip-"+t++,i=e("<div>").attr({id:r,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return e("<div>").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),e.fn.bgiframe&&i.bgiframe(),this.tooltips[r]=n,i},_find:function(t){var n=t.data("ui-tooltip-id");return n?e("#"+n):e()},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(n,r){var i=e.Event("blur");i.target=i.currentTarget=r[0],t.close(i,!0),e("#"+n).remove(),r.data("ui-tooltip-title")&&(r.attr("title",r.data("ui-tooltip-title")),r.removeData("ui-tooltip-title"))})}})})(jQuery); \ No newline at end of file diff --git a/web/extensions/jquery/themes/ui-lightness/images/animated-overlay.gif b/web/extensions/jquery/themes/ui-lightness/images/animated-overlay.gif new file mode 100644 index 0000000..d441f75 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/animated-overlay.gif differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png new file mode 100644 index 0000000..9001a8c Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png new file mode 100644 index 0000000..6e61b81 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_flat_10_000000_40x100.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_flat_10_000000_40x100.png new file mode 100644 index 0000000..6a6d427 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_flat_10_000000_40x100.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png new file mode 100644 index 0000000..99ecd14 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png new file mode 100644 index 0000000..c4706f2 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000..cdf8123 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png new file mode 100644 index 0000000..0d988a6 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png new file mode 100644 index 0000000..77d8825 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png new file mode 100644 index 0000000..b319f83 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-icons_222222_256x240.png b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000..c1cb117 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_222222_256x240.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-icons_228ef1_256x240.png b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_228ef1_256x240.png new file mode 100644 index 0000000..3a0140c Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_228ef1_256x240.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ef8c08_256x240.png b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ef8c08_256x240.png new file mode 100644 index 0000000..036ee07 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ef8c08_256x240.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffd27a_256x240.png b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffd27a_256x240.png new file mode 100644 index 0000000..8b6c058 Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffd27a_256x240.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffffff_256x240.png b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000..4f624bb Binary files /dev/null and b/web/extensions/jquery/themes/ui-lightness/images/ui-icons_ffffff_256x240.png differ diff --git a/web/extensions/jquery/themes/ui-lightness/jquery-ui.css b/web/extensions/jquery/themes/ui-lightness/jquery-ui.css new file mode 100644 index 0000000..f3335ec --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery-ui.css @@ -0,0 +1,1178 @@ +/*! jQuery UI - v1.10.4 - 2014-04-02 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin-top: 2px; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-noicons { + padding-left: .7em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} +.ui-dialog { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-menu { + list-style: none; + padding: 2px; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + margin-top: -3px; + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + padding: 0; + width: 100%; + /* support: IE10, see #8844 */ + list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); +} +.ui-menu .ui-menu-divider { + margin: 5px -2px 5px -2px; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-menu-item a { + text-decoration: none; + display: block; + padding: 2px .4em; + line-height: 1.5; + min-height: 0; /* support: IE7 */ + font-weight: normal; +} +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} + +.ui-menu .ui-state-disabled { + font-weight: normal; + margin: .4em 0 .2em; + line-height: 1.5; +} +.ui-menu .ui-state-disabled a { + cursor: default; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item a { + position: relative; + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: .2em; + left: .2em; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + position: static; + float: right; +} +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("images/animated-overlay.gif"); + height: 100%; + filter: alpha(opacity=25); + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* For IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to override default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertically center icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav .ui-tabs-anchor { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { + cursor: text; +} +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #dddddd; + background: #eeeeee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x; + color: #333333; +} +.ui-widget-content a { + color: #333333; +} +.ui-widget-header { + border: 1px solid #aaa; + background: #aaa; + color: #ffffff; + font-weight: bold; +} +.ui-widget-header a { + color: #ffffff; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #cccccc; + background: #f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #1c94c4; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #1c94c4; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #fbcb09; + background: #fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #c77405; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited { + color: #c77405; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #fbd850; + background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #eb8f00; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #eb8f00; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fed22f; + background: #ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x; + color: #363636; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat; + color: #ffffff; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #ffffff; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #ffffff; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_ffffff_256x240.png"); +} +.ui-state-default .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-active .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-highlight .ui-icon { + background-image: url("images/ui-icons_228ef1_256x240.png"); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_ffd27a_256x240.png"); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #666666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat; + opacity: .5; + filter: Alpha(Opacity=50); +} +.ui-widget-shadow { + margin: -5px 0 0 -5px; + padding: 5px; + background: #000000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x; + opacity: .2; + filter: Alpha(Opacity=20); + border-radius: 5px; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.accordion.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.accordion.css new file mode 100644 index 0000000..d36f910 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.accordion.css @@ -0,0 +1,38 @@ +/*! + * jQuery UI Accordion 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin-top: 2px; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-noicons { + padding-left: .7em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.all.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.all.css new file mode 100644 index 0000000..a0df169 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.all.css @@ -0,0 +1,12 @@ +/*! + * jQuery UI CSS Framework 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming + */ +@import "jquery.ui.base.css"; +@import "jquery.ui.theme.css"; diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.autocomplete.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.autocomplete.css new file mode 100644 index 0000000..5e2c750 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.autocomplete.css @@ -0,0 +1,16 @@ +/*! + * jQuery UI Autocomplete 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.base.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.base.css new file mode 100644 index 0000000..84c9507 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.base.css @@ -0,0 +1,25 @@ +/*! + * jQuery UI CSS Framework 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming + */ +@import url("jquery.ui.core.css"); + +@import url("jquery.ui.accordion.css"); +@import url("jquery.ui.autocomplete.css"); +@import url("jquery.ui.button.css"); +@import url("jquery.ui.datepicker.css"); +@import url("jquery.ui.dialog.css"); +@import url("jquery.ui.menu.css"); +@import url("jquery.ui.progressbar.css"); +@import url("jquery.ui.resizable.css"); +@import url("jquery.ui.selectable.css"); +@import url("jquery.ui.slider.css"); +@import url("jquery.ui.spinner.css"); +@import url("jquery.ui.tabs.css"); +@import url("jquery.ui.tooltip.css"); diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.button.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.button.css new file mode 100644 index 0000000..52d6c13 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.button.css @@ -0,0 +1,114 @@ +/*! + * jQuery UI Button 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.core.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.core.css new file mode 100644 index 0000000..04d6052 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.core.css @@ -0,0 +1,93 @@ +/*! + * jQuery UI CSS Framework 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.datepicker.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.datepicker.css new file mode 100644 index 0000000..58bc5d6 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.datepicker.css @@ -0,0 +1,178 @@ +/*! + * jQuery UI Datepicker 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month-year { + width: 100%; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.dialog.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.dialog.css new file mode 100644 index 0000000..533d606 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.dialog.css @@ -0,0 +1,69 @@ +/*! + * jQuery UI Dialog 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 21px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.menu.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.menu.css new file mode 100644 index 0000000..c48ab33 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.menu.css @@ -0,0 +1,79 @@ +/*! + * jQuery UI Menu 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style: none; + padding: 2px; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + margin-top: -3px; + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + padding: 0; + width: 100%; + /* support: IE10, see #8844 */ + list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); +} +.ui-menu .ui-menu-divider { + margin: 5px -2px 5px -2px; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-menu-item a { + text-decoration: none; + display: block; + padding: 2px .4em; + line-height: 1.5; + min-height: 0; /* support: IE7 */ + font-weight: normal; +} +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} + +.ui-menu .ui-state-disabled { + font-weight: normal; + margin: .4em 0 .2em; + line-height: 1.5; +} +.ui-menu .ui-state-disabled a { + cursor: default; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item a { + position: relative; + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: .2em; + left: .2em; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + position: static; + float: right; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.progressbar.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.progressbar.css new file mode 100644 index 0000000..958e231 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.progressbar.css @@ -0,0 +1,28 @@ +/*! + * jQuery UI Progressbar 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("images/animated-overlay.gif"); + height: 100%; + filter: alpha(opacity=25); + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.resizable.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.resizable.css new file mode 100644 index 0000000..c46e935 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.resizable.css @@ -0,0 +1,78 @@ +/*! + * jQuery UI Resizable 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.selectable.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.selectable.css new file mode 100644 index 0000000..274cd44 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.selectable.css @@ -0,0 +1,15 @@ +/*! + * jQuery UI Selectable 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.slider.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.slider.css new file mode 100644 index 0000000..f2d4952 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.slider.css @@ -0,0 +1,73 @@ +/*! + * jQuery UI Slider 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* For IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.spinner.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.spinner.css new file mode 100644 index 0000000..9a92c9f --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.spinner.css @@ -0,0 +1,65 @@ +/*! + * jQuery UI Spinner 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Spinner#theming + */ +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to overide default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertical centre icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.tabs.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.tabs.css new file mode 100644 index 0000000..26f9c27 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.tabs.css @@ -0,0 +1,52 @@ +/*! + * jQuery UI Tabs 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav li a { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active a, +.ui-tabs .ui-tabs-nav li.ui-state-disabled a, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading a { + cursor: text; +} +.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.theme.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.theme.css new file mode 100644 index 0000000..a28a662 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.theme.css @@ -0,0 +1,406 @@ +/*! + * jQuery UI CSS Framework 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #dddddd; + background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; + color: #333333; +} +.ui-widget-content a { + color: #333333; +} +.ui-widget-header { + border: 1px solid #e78f08; + background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; + color: #ffffff; + font-weight: bold; +} +.ui-widget-header a { + color: #ffffff; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #cccccc; + background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; + font-weight: bold; + color: #1c94c4; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #1c94c4; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #fbcb09; + background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; + font-weight: bold; + color: #c77405; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited { + color: #c77405; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #fbd850; + background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; + font-weight: bold; + color: #eb8f00; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #eb8f00; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fed22f; + background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; + color: #363636; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; + color: #ffffff; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #ffffff; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #ffffff; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png); +} +.ui-widget-header .ui-icon { + background-image: url(images/ui-icons_ffffff_256x240.png); +} +.ui-state-default .ui-icon { + background-image: url(images/ui-icons_ef8c08_256x240.png); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url(images/ui-icons_ef8c08_256x240.png); +} +.ui-state-active .ui-icon { + background-image: url(images/ui-icons_ef8c08_256x240.png); +} +.ui-state-highlight .ui-icon { + background-image: url(images/ui-icons_228ef1_256x240.png); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url(images/ui-icons_ffd27a_256x240.png); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; + opacity: .5; + filter: Alpha(Opacity=50); +} +.ui-widget-shadow { + margin: -5px 0 0 -5px; + padding: 5px; + background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; + opacity: .2; + filter: Alpha(Opacity=20); + border-radius: 5px; +} diff --git a/web/extensions/jquery/themes/ui-lightness/jquery.ui.tooltip.css b/web/extensions/jquery/themes/ui-lightness/jquery.ui.tooltip.css new file mode 100644 index 0000000..d7632a4 --- /dev/null +++ b/web/extensions/jquery/themes/ui-lightness/jquery.ui.tooltip.css @@ -0,0 +1,19 @@ +/*! + * jQuery UI Tooltip 1.10.3 + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} diff --git a/web/favicon.ico b/web/favicon.ico new file mode 100644 index 0000000..6aa44e4 Binary files /dev/null and b/web/favicon.ico differ diff --git a/web/framework-1.0.8/.htaccess b/web/framework-1.0.8/.htaccess new file mode 100644 index 0000000..e019832 --- /dev/null +++ b/web/framework-1.0.8/.htaccess @@ -0,0 +1 @@ +deny from all diff --git a/web/framework-1.0.8/YiiBase.php b/web/framework-1.0.8/YiiBase.php new file mode 100644 index 0000000..a41931e --- /dev/null +++ b/web/framework-1.0.8/YiiBase.php @@ -0,0 +1,684 @@ +<?php +/** + * YiiBase class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: YiiBase.php 1316 2009-08-09 04:11:25Z qiang.xue $ + * @package system + * @since 1.0 + */ + +/** + * Gets the application start timestamp. + */ +defined('YII_BEGIN_TIME') or define('YII_BEGIN_TIME',microtime(true)); +/** + * This constant defines whether the application should be in debug mode or not. Defaults to false. + */ +defined('YII_DEBUG') or define('YII_DEBUG',false); +/** + * This constant defines how much call stack information (file name and line number) should be logged by Yii::trace(). + * Defaults to 0, meaning no backtrace information. If it is greater than 0, + * at most that number of call stacks will be logged. Note, only user application call stacks are considered. + */ +defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',0); +/** + * This constant defines whether exception handling should be enabled. Defaults to true. + */ +defined('YII_ENABLE_EXCEPTION_HANDLER') or define('YII_ENABLE_EXCEPTION_HANDLER',true); +/** + * This constant defines whether error handling should be enabled. Defaults to true. + */ +defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER',true); +/** + * Defines the Yii framework installation path. + */ +defined('YII_PATH') or define('YII_PATH',dirname(__FILE__)); + +/** + * YiiBase is a helper class serving common framework functionalities. + * + * Do not use YiiBase directly. Instead, use its child class {@link Yii} where + * you can customize methods of YiiBase. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: YiiBase.php 1316 2009-08-09 04:11:25Z qiang.xue $ + * @package system + * @since 1.0 + */ +class YiiBase +{ + private static $_aliases=array('system'=>YII_PATH); // alias => path + private static $_imports=array(); // alias => class name or directory + private static $_classes=array(); + private static $_includePaths; // list of include paths + private static $_app; + private static $_logger; + + + /** + * @return string the version of Yii framework + */ + public static function getVersion() + { + return '1.0.8'; + } + + /** + * Creates a Web application instance. + * @param mixed application configuration. + * If a string, it is treated as the path of the file that contains the configuration; + * If an array, it is the actual configuration information. + * Please make sure you specify the {@link CApplication::basePath basePath} property in the configuration, + * which should point to the directory containing all application logic, template and data. + * If not, the directory will be defaulted to 'protected'. + */ + public static function createWebApplication($config=null) + { + return new CWebApplication($config); + } + + /** + * Creates a console application instance. + * @param mixed application configuration. + * If a string, it is treated as the path of the file that contains the configuration; + * If an array, it is the actual configuration information. + * Please make sure you specify the {@link CApplication::basePath basePath} property in the configuration, + * which should point to the directory containing all application logic, template and data. + * If not, the directory will be defaulted to 'protected'. + */ + public static function createConsoleApplication($config=null) + { + return new CConsoleApplication($config); + } + + /** + * @return CApplication the application singleton, null if the singleton has not been created yet. + */ + public static function app() + { + return self::$_app; + } + + /** + * Stores the application instance in the class static member. + * This method helps implement a singleton pattern for CApplication. + * Repeated invocation of this method or the CApplication constructor + * will cause the throw of an exception. + * To retrieve the application instance, use {@link app()}. + * @param CApplication the application instance. If this is null, the existing + * application singleton will be removed. + * @throws CException if multiple application instances are registered. + */ + public static function setApplication($app) + { + if(self::$_app===null || $app===null) + self::$_app=$app; + else + throw new CException(Yii::t('yii','Yii application can only be created once.')); + } + + /** + * @return string the path of the framework + */ + public static function getFrameworkPath() + { + return YII_PATH; + } + + /** + * Creates an object and initializes it based on the given configuration. + * + * The specified configuration can be either a string or an array. + * If the former, the string is treated as the object type which can + * be either the class name or {@link YiiBase::getPathOfAlias class path alias}. + * If the latter, the 'class' element is treated as the object type, + * and the rest name-value pairs in the array are used to initialize + * the corresponding object properties. + * + * Any additional parameters passed to this method will be + * passed to the constructor of the object being created. + * + * NOTE: the array-typed configuration has been supported since version 1.0.1. + * + * @param mixed the configuration. It can be either a string or an array. + * @return mixed the created object + * @throws CException if the configuration does not have a 'class' element. + */ + public static function createComponent($config) + { + if(is_string($config)) + { + $type=$config; + $config=array(); + } + else if(isset($config['class'])) + { + $type=$config['class']; + unset($config['class']); + } + else + throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.')); + + if(!class_exists($type,false)) + $type=Yii::import($type,true); + + if(($n=func_num_args())>1) + { + $args=func_get_args(); + if($n===2) + $object=new $type($args[1]); + else if($n===3) + $object=new $type($args[1],$args[2]); + else if($n===4) + $object=new $type($args[1],$args[2],$args[3]); + else + { + unset($args[0]); + $class=new ReflectionClass($type); + // Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+ + // $object=$class->newInstanceArgs($args); + $object=call_user_func_array(array($class,'newInstance'),$args); + } + } + else + $object=new $type; + + foreach($config as $key=>$value) + $object->$key=$value; + + return $object; + } + + /** + * Imports the definition of a class or a directory of class files. + * + * Path aliases are used to refer to the class file or directory being imported. + * If importing a path alias ending with '.*', the alias is considered as a directory + * which will be added to the PHP include paths; Otherwise, the alias is translated + * to the path of a class file which is included when needed. + * + * For example, importing 'system.web.*' will add the 'web' directory of the framework + * to the PHP include paths; while importing 'system.web.CController' will include + * the class file 'web/CController.php' when needed. + * + * The same alias can be imported multiple times, but only the first time is effective. + * + * @param string path alias to be imported + * @param boolean whether to include the class file immediately. If false, the class file + * will be included only when the class is being used. + * @return string the class name or the directory that this alias refers to + * @throws CException if the alias is invalid + */ + public static function import($alias,$forceInclude=false) + { + if(isset(self::$_imports[$alias])) // previously imported + return self::$_imports[$alias]; + + if(class_exists($alias,false) || interface_exists($alias,false)) + return self::$_imports[$alias]=$alias; + + if(isset(self::$_coreClasses[$alias]) || ($pos=strrpos($alias,'.'))===false) // a simple class name + { + self::$_imports[$alias]=$alias; + if($forceInclude) + { + if(isset(self::$_coreClasses[$alias])) // a core class + require(YII_PATH.self::$_coreClasses[$alias]); + else + require($alias.'.php'); + } + return $alias; + } + + if(($className=(string)substr($alias,$pos+1))!=='*' && (class_exists($className,false) || interface_exists($className,false))) + return self::$_imports[$alias]=$className; + + if(($path=self::getPathOfAlias($alias))!==false) + { + if($className!=='*') + { + self::$_imports[$alias]=$className; + if($forceInclude) + require($path.'.php'); + else + self::$_classes[$className]=$path.'.php'; + return $className; + } + else // a directory + { + if(self::$_includePaths===null) + { + self::$_includePaths=array_unique(explode(PATH_SEPARATOR,get_include_path())); + if(($pos=array_search('.',self::$_includePaths,true))!==false) + unset(self::$_includePaths[$pos]); + } + array_unshift(self::$_includePaths,$path); + set_include_path('.'.PATH_SEPARATOR.implode(PATH_SEPARATOR,self::$_includePaths)); + return self::$_imports[$alias]=$path; + } + } + else + throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.', + array('{alias}'=>$alias))); + } + + /** + * Translates an alias into a file path. + * Note, this method does not ensure the existence of the resulting file path. + * It only checks if the root alias is valid or not. + * @param string alias (e.g. system.web.CController) + * @return mixed file path corresponding to the alias, false if the alias is invalid. + */ + public static function getPathOfAlias($alias) + { + if(isset(self::$_aliases[$alias])) + return self::$_aliases[$alias]; + else if(($pos=strpos($alias,'.'))!==false) + { + $rootAlias=substr($alias,0,$pos); + if(isset(self::$_aliases[$rootAlias])) + return self::$_aliases[$alias]=rtrim(self::$_aliases[$rootAlias].DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,substr($alias,$pos+1)),'*'.DIRECTORY_SEPARATOR); + else if(self::$_app instanceof CWebApplication) + { + if(self::$_app->findModule($rootAlias)!==null) + return self::getPathOfAlias($alias); + } + } + return false; + } + + /** + * Create a path alias. + * Note, this method neither checks the existence of the path nor normalizes the path. + * @param string alias to the path + * @param string the path corresponding to the alias. If this is null, the corresponding + * path alias will be removed. + */ + public static function setPathOfAlias($alias,$path) + { + if(empty($path)) + unset(self::$_aliases[$alias]); + else + self::$_aliases[$alias]=rtrim($path,'\\/'); + } + + /** + * Class autoload loader. + * This method is provided to be invoked within an __autoload() magic method. + * @param string class name + * @return boolean whether the class has been loaded successfully + */ + public static function autoload($className) + { + if(strstr($className, 'CAS_')) return false; + + // use include so that the error PHP file may appear + if(isset(self::$_coreClasses[$className])) + include(YII_PATH.self::$_coreClasses[$className]); + else if(isset(self::$_classes[$className])) + include(self::$_classes[$className]); + else + { + $classfile = GetSSModulePath("{$className}Model"); + if($classfile) + { + include($classfile); + return class_exists($className,false) || interface_exists($className,false); + } + + include($className.'.php'); + return class_exists($className,false) || interface_exists($className,false); + } + return true; + } + + /** + * Writes a trace message. + * This method will only log a message when the application is in debug mode. + * @param string message to be logged + * @param string category of the message + * @see log + */ + public static function trace($msg,$category='application') + { + if(YII_DEBUG) + { + if(YII_TRACE_LEVEL>0) + { + $traces=debug_backtrace(); + $count=0; + foreach($traces as $trace) + { + if(isset($trace['file'],$trace['line'])) + { + $className=substr(basename($trace['file']),0,-4); + if(!isset(self::$_coreClasses[$className]) && $className!=='YiiBase') + { + $msg.="\nin ".$trace['file'].' ('.$trace['line'].')'; + if(++$count>=YII_TRACE_LEVEL) + break; + } + } + } + } + self::log($msg,CLogger::LEVEL_TRACE,$category); + } + } + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link CLogger::getLogs} + * and may be recorded in different media, such as file, email, database, using + * {@link CLogRouter}. + * @param string message to be logged + * @param string level of the message (e.g. 'trace', 'warning', 'error'). It is case-insensitive. + * @param string category of the message (e.g. 'system.web'). It is case-insensitive. + */ + public static function log($msg,$level=CLogger::LEVEL_INFO,$category='application') + { + if(self::$_logger===null) + self::$_logger=new CLogger; + self::$_logger->log($msg,$level,$category); + } + + /** + * Marks the begin of a code block for profiling. + * This has to be matched with a call to {@link endProfile()} with the same token. + * The begin- and end- calls must also be properly nested, e.g., + * <pre> + * Yii::beginProfile('block1'); + * Yii::beginProfile('block2'); + * Yii::endProfile('block2'); + * Yii::endProfile('block1'); + * </pre> + * The following sequence is not valid: + * <pre> + * Yii::beginProfile('block1'); + * Yii::beginProfile('block2'); + * Yii::endProfile('block1'); + * Yii::endProfile('block2'); + * </pre> + * @param string token for the code block + * @param string the category of this log message + * @see endProfile + */ + public static function beginProfile($token,$category='application') + { + self::log('begin:'.$token,CLogger::LEVEL_PROFILE,$category); + } + + /** + * Marks the end of a code block for profiling. + * This has to be matched with a previous call to {@link beginProfile()} with the same token. + * @param string token for the code block + * @param string the category of this log message + * @see beginProfile + */ + public static function endProfile($token,$category='application') + { + self::log('end:'.$token,CLogger::LEVEL_PROFILE,$category); + } + + /** + * @return CLogger message logger + */ + public static function getLogger() + { + if(self::$_logger!==null) + return self::$_logger; + else + return self::$_logger=new CLogger; + } + + /** + * @return string a string that can be displayed on your Web page showing Powered-by-Yii information + */ + public static function powered() + { + return 'Powered by <a href="http://www.yiiframework.com/" target="_blank">Yii Framework</a>.'; + } + + /** + * Translates a message to the specified language. + * Starting from version 1.0.2, this method supports choice format (see {@link CChoiceFormat}), + * i.e., the message returned will be chosen from a few candidates according to the given + * number value. This feature is mainly used to solve plural format issue in case + * a message has different plural forms in some languages. + * @param string message category. Please use only word letters. Note, category 'yii' is + * reserved for Yii framework core code use. See {@link CPhpMessageSource} for + * more interpretation about message category. + * @param string the original message + * @param array parameters to be applied to the message using <code>strtr</code>. + * Starting from version 1.0.2, the first parameter can be a number without key. + * And in this case, the method will call {@link CChoiceFormat::format} to choose + * an appropriate message translation. + * @param string which message source application component to use. + * Defaults to null, meaning using 'coreMessages' for messages belonging to + * the 'yii' category and using 'messages' for the rest messages. + * @param string the target language. If null (default), the {@link CApplication::getLanguage application language} will be used. + * This parameter has been available since version 1.0.3. + * @return string the translated message + * @see CMessageSource + */ + public static function t($category,$message,$params=array(),$source=null,$language=null) + { + if(self::$_app!==null) + { + if($source===null) + $source=$category==='yii'?'coreMessages':'messages'; + if(($source=self::$_app->getComponent($source))!==null) + $message=$source->translate($category,$message,$language); + } + if($params===array()) + return $message; + if(isset($params[0])) // number choice + { + $message=CChoiceFormat::format($message,$params[0]); + unset($params[0]); + } + return $params!==array() ? strtr($message,$params) : $message; + } + + /** + * @var array class map for core Yii classes. + * NOTE, DO NOT MODIFY THIS ARRAY MANUALLY. IF YOU CHANGE OR ADD SOME CORE CLASSES, + * PLEASE RUN 'build autoload' COMMAND TO UPDATE THIS ARRAY. + */ + private static $_coreClasses=array( + 'CApplication' => '/base/CApplication.php', + 'CApplicationComponent' => '/base/CApplicationComponent.php', + 'CBehavior' => '/base/CBehavior.php', + 'CComponent' => '/base/CComponent.php', + 'CErrorEvent' => '/base/CErrorEvent.php', + 'CErrorHandler' => '/base/CErrorHandler.php', + 'CException' => '/base/CException.php', + 'CExceptionEvent' => '/base/CExceptionEvent.php', + 'CHttpException' => '/base/CHttpException.php', + 'CModel' => '/base/CModel.php', + 'CModelBehavior' => '/base/CModelBehavior.php', + 'CModelEvent' => '/base/CModelEvent.php', + 'CModule' => '/base/CModule.php', + 'CSecurityManager' => '/base/CSecurityManager.php', + 'CStatePersister' => '/base/CStatePersister.php', + 'CApcCache' => '/caching/CApcCache.php', + 'CCache' => '/caching/CCache.php', + 'CDbCache' => '/caching/CDbCache.php', + 'CDummyCache' => '/caching/CDummyCache.php', + 'CEAcceleratorCache' => '/caching/CEAcceleratorCache.php', + 'CFileCache' => '/caching/CFileCache.php', + 'CMemCache' => '/caching/CMemCache.php', + 'CXCache' => '/caching/CXCache.php', + 'CZendDataCache' => '/caching/CZendDataCache.php', + 'CCacheDependency' => '/caching/dependencies/CCacheDependency.php', + 'CChainedCacheDependency' => '/caching/dependencies/CChainedCacheDependency.php', + 'CDbCacheDependency' => '/caching/dependencies/CDbCacheDependency.php', + 'CDirectoryCacheDependency' => '/caching/dependencies/CDirectoryCacheDependency.php', + 'CExpressionDependency' => '/caching/dependencies/CExpressionDependency.php', + 'CFileCacheDependency' => '/caching/dependencies/CFileCacheDependency.php', + 'CGlobalStateCacheDependency' => '/caching/dependencies/CGlobalStateCacheDependency.php', + 'CAttributeCollection' => '/collections/CAttributeCollection.php', + 'CConfiguration' => '/collections/CConfiguration.php', + 'CList' => '/collections/CList.php', + 'CListIterator' => '/collections/CListIterator.php', + 'CMap' => '/collections/CMap.php', + 'CMapIterator' => '/collections/CMapIterator.php', + 'CQueue' => '/collections/CQueue.php', + 'CQueueIterator' => '/collections/CQueueIterator.php', + 'CStack' => '/collections/CStack.php', + 'CStackIterator' => '/collections/CStackIterator.php', + 'CTypedList' => '/collections/CTypedList.php', + 'CConsoleApplication' => '/console/CConsoleApplication.php', + 'CConsoleCommand' => '/console/CConsoleCommand.php', + 'CConsoleCommandRunner' => '/console/CConsoleCommandRunner.php', + 'CHelpCommand' => '/console/CHelpCommand.php', + 'CDbCommand' => '/db/CDbCommand.php', + 'CDbConnection' => '/db/CDbConnection.php', + 'CDbDataReader' => '/db/CDbDataReader.php', + 'CDbException' => '/db/CDbException.php', + 'CDbTransaction' => '/db/CDbTransaction.php', + 'CActiveFinder' => '/db/ar/CActiveFinder.php', + 'CActiveRecord' => '/db/ar/CActiveRecord.php', + 'CActiveRecordBehavior' => '/db/ar/CActiveRecordBehavior.php', + 'CDbColumnSchema' => '/db/schema/CDbColumnSchema.php', + 'CDbCommandBuilder' => '/db/schema/CDbCommandBuilder.php', + 'CDbCriteria' => '/db/schema/CDbCriteria.php', + 'CDbExpression' => '/db/schema/CDbExpression.php', + 'CDbSchema' => '/db/schema/CDbSchema.php', + 'CDbTableSchema' => '/db/schema/CDbTableSchema.php', + 'CMssqlColumnSchema' => '/db/schema/mssql/CMssqlColumnSchema.php', + 'CMssqlCommandBuilder' => '/db/schema/mssql/CMssqlCommandBuilder.php', + 'CMssqlPdoAdapter' => '/db/schema/mssql/CMssqlPdoAdapter.php', + 'CMssqlSchema' => '/db/schema/mssql/CMssqlSchema.php', + 'CMssqlTableSchema' => '/db/schema/mssql/CMssqlTableSchema.php', + 'CMysqlColumnSchema' => '/db/schema/mysql/CMysqlColumnSchema.php', + 'CMysqlSchema' => '/db/schema/mysql/CMysqlSchema.php', + 'CMysqlTableSchema' => '/db/schema/mysql/CMysqlTableSchema.php', + 'COciColumnSchema' => '/db/schema/oci/COciColumnSchema.php', + 'COciCommandBuilder' => '/db/schema/oci/COciCommandBuilder.php', + 'COciSchema' => '/db/schema/oci/COciSchema.php', + 'COciTableSchema' => '/db/schema/oci/COciTableSchema.php', + 'CPgsqlColumnSchema' => '/db/schema/pgsql/CPgsqlColumnSchema.php', + 'CPgsqlSchema' => '/db/schema/pgsql/CPgsqlSchema.php', + 'CPgsqlTableSchema' => '/db/schema/pgsql/CPgsqlTableSchema.php', + 'CSqliteColumnSchema' => '/db/schema/sqlite/CSqliteColumnSchema.php', + 'CSqliteCommandBuilder' => '/db/schema/sqlite/CSqliteCommandBuilder.php', + 'CSqliteSchema' => '/db/schema/sqlite/CSqliteSchema.php', + 'CChoiceFormat' => '/i18n/CChoiceFormat.php', + 'CDateFormatter' => '/i18n/CDateFormatter.php', + 'CDbMessageSource' => '/i18n/CDbMessageSource.php', + 'CGettextMessageSource' => '/i18n/CGettextMessageSource.php', + 'CLocale' => '/i18n/CLocale.php', + 'CMessageSource' => '/i18n/CMessageSource.php', + 'CNumberFormatter' => '/i18n/CNumberFormatter.php', + 'CPhpMessageSource' => '/i18n/CPhpMessageSource.php', + 'CGettextFile' => '/i18n/gettext/CGettextFile.php', + 'CGettextMoFile' => '/i18n/gettext/CGettextMoFile.php', + 'CGettextPoFile' => '/i18n/gettext/CGettextPoFile.php', + 'CDbLogRoute' => '/logging/CDbLogRoute.php', + 'CEmailLogRoute' => '/logging/CEmailLogRoute.php', + 'CFileLogRoute' => '/logging/CFileLogRoute.php', + 'CLogFilter' => '/logging/CLogFilter.php', + 'CLogRoute' => '/logging/CLogRoute.php', + 'CLogRouter' => '/logging/CLogRouter.php', + 'CLogger' => '/logging/CLogger.php', + 'CProfileLogRoute' => '/logging/CProfileLogRoute.php', + 'CWebLogRoute' => '/logging/CWebLogRoute.php', + 'CDateTimeParser' => '/utils/CDateTimeParser.php', + 'CFileHelper' => '/utils/CFileHelper.php', + 'CMarkdownParser' => '/utils/CMarkdownParser.php', + 'CPropertyValue' => '/utils/CPropertyValue.php', + 'CTimestamp' => '/utils/CTimestamp.php', + 'CVarDumper' => '/utils/CVarDumper.php', + 'CCaptchaValidator' => '/validators/CCaptchaValidator.php', + 'CCompareValidator' => '/validators/CCompareValidator.php', + 'CDefaultValueValidator' => '/validators/CDefaultValueValidator.php', + 'CEmailValidator' => '/validators/CEmailValidator.php', + 'CExistValidator' => '/validators/CExistValidator.php', + 'CFileValidator' => '/validators/CFileValidator.php', + 'CFilterValidator' => '/validators/CFilterValidator.php', + 'CInlineValidator' => '/validators/CInlineValidator.php', + 'CNumberValidator' => '/validators/CNumberValidator.php', + 'CRangeValidator' => '/validators/CRangeValidator.php', + 'CRegularExpressionValidator' => '/validators/CRegularExpressionValidator.php', + 'CRequiredValidator' => '/validators/CRequiredValidator.php', + 'CStringValidator' => '/validators/CStringValidator.php', + 'CTypeValidator' => '/validators/CTypeValidator.php', + 'CUniqueValidator' => '/validators/CUniqueValidator.php', + 'CUrlValidator' => '/validators/CUrlValidator.php', + 'CValidator' => '/validators/CValidator.php', + 'CAssetManager' => '/web/CAssetManager.php', + 'CBaseController' => '/web/CBaseController.php', + 'CCacheHttpSession' => '/web/CCacheHttpSession.php', + 'CClientScript' => '/web/CClientScript.php', + 'CController' => '/web/CController.php', + 'CDbHttpSession' => '/web/CDbHttpSession.php', + 'CExtController' => '/web/CExtController.php', + 'CFormModel' => '/web/CFormModel.php', + 'CHttpCookie' => '/web/CHttpCookie.php', + 'CHttpRequest' => '/web/CHttpRequest.php', + 'CHttpSession' => '/web/CHttpSession.php', + 'CHttpSessionIterator' => '/web/CHttpSessionIterator.php', + 'COutputEvent' => '/web/COutputEvent.php', + 'CPagination' => '/web/CPagination.php', + 'CSort' => '/web/CSort.php', + 'CTheme' => '/web/CTheme.php', + 'CThemeManager' => '/web/CThemeManager.php', + 'CUploadedFile' => '/web/CUploadedFile.php', + 'CUrlManager' => '/web/CUrlManager.php', + 'CWebApplication' => '/web/CWebApplication.php', + 'CWebModule' => '/web/CWebModule.php', + 'CAction' => '/web/actions/CAction.php', + 'CInlineAction' => '/web/actions/CInlineAction.php', + 'CViewAction' => '/web/actions/CViewAction.php', + 'CAccessControlFilter' => '/web/auth/CAccessControlFilter.php', + 'CAuthAssignment' => '/web/auth/CAuthAssignment.php', + 'CAuthItem' => '/web/auth/CAuthItem.php', + 'CAuthManager' => '/web/auth/CAuthManager.php', + 'CBaseUserIdentity' => '/web/auth/CBaseUserIdentity.php', + 'CDbAuthManager' => '/web/auth/CDbAuthManager.php', + 'CPhpAuthManager' => '/web/auth/CPhpAuthManager.php', + 'CUserIdentity' => '/web/auth/CUserIdentity.php', + 'CWebUser' => '/web/auth/CWebUser.php', + 'CFilter' => '/web/filters/CFilter.php', + 'CFilterChain' => '/web/filters/CFilterChain.php', + 'CInlineFilter' => '/web/filters/CInlineFilter.php', + 'CGoogleApi' => '/web/helpers/CGoogleApi.php', + 'CHtml' => '/web/helpers/CHtml.php', + 'CJSON' => '/web/helpers/CJSON.php', + 'CJavaScript' => '/web/helpers/CJavaScript.php', + 'CPradoViewRenderer' => '/web/renderers/CPradoViewRenderer.php', + 'CViewRenderer' => '/web/renderers/CViewRenderer.php', + 'CWebService' => '/web/services/CWebService.php', + 'CWebServiceAction' => '/web/services/CWebServiceAction.php', + 'CWsdlGenerator' => '/web/services/CWsdlGenerator.php', + 'CAutoComplete' => '/web/widgets/CAutoComplete.php', + 'CClipWidget' => '/web/widgets/CClipWidget.php', + 'CContentDecorator' => '/web/widgets/CContentDecorator.php', + 'CFilterWidget' => '/web/widgets/CFilterWidget.php', + 'CFlexWidget' => '/web/widgets/CFlexWidget.php', + 'CHtmlPurifier' => '/web/widgets/CHtmlPurifier.php', + 'CInputWidget' => '/web/widgets/CInputWidget.php', + 'CMarkdown' => '/web/widgets/CMarkdown.php', + 'CMaskedTextField' => '/web/widgets/CMaskedTextField.php', + 'CMultiFileUpload' => '/web/widgets/CMultiFileUpload.php', + 'COutputCache' => '/web/widgets/COutputCache.php', + 'COutputProcessor' => '/web/widgets/COutputProcessor.php', + 'CStarRating' => '/web/widgets/CStarRating.php', + 'CTabView' => '/web/widgets/CTabView.php', + 'CTextHighlighter' => '/web/widgets/CTextHighlighter.php', + 'CTreeView' => '/web/widgets/CTreeView.php', + 'CWidget' => '/web/widgets/CWidget.php', + 'CCaptcha' => '/web/widgets/captcha/CCaptcha.php', + 'CCaptchaAction' => '/web/widgets/captcha/CCaptchaAction.php', + 'CBasePager' => '/web/widgets/pagers/CBasePager.php', + 'CLinkPager' => '/web/widgets/pagers/CLinkPager.php', + 'CListPager' => '/web/widgets/pagers/CListPager.php', + ); +} + +spl_autoload_register(array('YiiBase','autoload')); +require(YII_PATH.'/base/interfaces.php'); diff --git a/web/framework-1.0.8/base/CApplication.php b/web/framework-1.0.8/base/CApplication.php new file mode 100644 index 0000000..5c3834b --- /dev/null +++ b/web/framework-1.0.8/base/CApplication.php @@ -0,0 +1,718 @@ +<?php +/** + * CApplication class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CApplication is the base class for all application classes. + * + * An application serves as the global context that the user request + * is being processed. It manages a set of application components that + * provide specific functionalities to the whole application. + * + * The core application components provided by CApplication are the following: + * <ul> + * <li>{@link getErrorHandler errorHandler}: handles PHP errors and + * uncaught exceptions. This application component is dynamically loaded when needed.</li> + * <li>{@link getSecurityManager securityManager}: provides security-related + * services, such as hashing, encryption. This application component is dynamically + * loaded when needed.</li> + * <li>{@link getStatePersister statePersister}: provides global state + * persistence method. This application component is dynamically loaded when needed.</li> + * <li>{@link getCache cache}: provides caching feature. This application component is + * disabled by default.</li> + * <li>{@link getMessages messages}: provides the message source for translating + * application messages. This application component is dynamically loaded when needed.</li> + * <li>{@link getCoreMessages coreMessages}: provides the message source for translating + * Yii framework messages. This application component is dynamically loaded when needed.</li> + * </ul> + * + * CApplication will undergo the following lifecycles when processing a user request: + * <ol> + * <li>load application configuration;</li> + * <li>set up class autoloader and error handling;</li> + * <li>load static application components;</li> + * <li>{@link onBeginRequest}: preprocess the user request;</li> + * <li>{@link processRequest}: process the user request;</li> + * <li>{@link onEndRequest}: postprocess the user request;</li> + * </ol> + * + * Starting from lifecycle 3, if a PHP error or an uncaught exception occurs, + * the application will switch to its error handling logic and jump to step 6 afterwards. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CApplication.php 1301 2009-08-07 13:19:55Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +abstract class CApplication extends CModule +{ + /** + * @var string the application name. Defaults to 'My Application'. + */ + public $name='My Application'; + /** + * @var string the charset currently used for the application. Defaults to 'UTF-8'. + */ + public $charset='UTF-8'; + /** + * @var string the language that the application is written in. This mainly refers to + * the language that the messages and view files are in. Defaults to 'en_us' (US English). + */ + public $sourceLanguage='en_us'; + + private $_id; + private $_basePath; + private $_runtimePath; + private $_extensionPath; + private $_globalState; + private $_stateChanged; + private $_ended=false; + private $_language; + + /** + * Processes the request. + * This is the place where the actual request processing work is done. + * Derived classes should override this method. + */ + abstract public function processRequest(); + + /** + * Constructor. + * @param mixed application configuration. + * If a string, it is treated as the path of the file that contains the configuration; + * If an array, it is the actual configuration information. + * Please make sure you specify the {@link getBasePath basePath} property in the configuration, + * which should point to the directory containing all application logic, template and data. + * If not, the directory will be defaulted to 'protected'. + */ + public function __construct($config=null) + { + Yii::setApplication($this); + + // set basePath at early as possible to avoid trouble + if(is_string($config)) + $config=require($config); + if(isset($config['basePath'])) + { + $this->setBasePath($config['basePath']); + unset($config['basePath']); + } + else + $this->setBasePath('protected'); + Yii::setPathOfAlias('application',$this->getBasePath()); + Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME'])); + Yii::setPathOfAlias('extensions',dirname($_SERVER['SCRIPT_FILENAME']).DIRECTORY_SEPARATOR.'extensions'); + + $this->preinit(); + + $this->initSystemHandlers(); + $this->registerCoreComponents(); + + $this->configure($config); + $this->attachBehaviors($this->behaviors); + $this->preloadComponents(); + + $this->init(); + } + + + /** + * Runs the application. + * This method loads static application components. Derived classes usually overrides this + * method to do more application-specific tasks. + * Remember to call the parent implementation so that static application components are loaded. + */ + public function run() + { + $this->onBeginRequest(new CEvent($this)); + $this->processRequest(); + $this->onEndRequest(new CEvent($this)); + } + + /** + * Terminates the application. + * This method replaces PHP's exit() function by calling + * {@link onEndRequest} before exiting. + * @param integer exit status (value 0 means normal exit while other values mean abnormal exit). + */ + public function end($status=0) + { + $this->onEndRequest(new CEvent($this)); + exit($status); + } + + /** + * Raised right BEFORE the application processes the request. + * @param CEvent the event parameter + */ + public function onBeginRequest($event) + { + $this->raiseEvent('onBeginRequest',$event); + } + + /** + * Raised right AFTER the application processes the request. + * @param CEvent the event parameter + */ + public function onEndRequest($event) + { + if(!$this->_ended) + { + $this->_ended=true; + $this->raiseEvent('onEndRequest',$event); + } + } + + /** + * @return string a unique identifier for the application. + */ + public function getId() + { + if($this->_id!==null) + return $this->_id; + else + return $this->_id=sprintf('%x',crc32($this->getBasePath().$this->name)); + } + + /** + * @param string a unique identifier for the application. + */ + public function setId($id) + { + $this->_id=$id; + } + + /** + * @return string the root directory of the application. Defaults to 'protected'. + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * Sets the root directory of the application. + * This method can only be invoked at the begin of the constructor. + * @param string the root directory of the application. + * @throws CException if the directory does not exist. + */ + public function setBasePath($path) + { + if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath)) + throw new CException(Yii::t('yii','Application base path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * @return string the directory that stores runtime files. Defaults to 'protected/runtime'. + */ + public function getRuntimePath() + { + if($this->_runtimePath!==null) + return $this->_runtimePath; + else + { + $this->setRuntimePath($this->getBasePath().DIRECTORY_SEPARATOR.'runtime'); + return $this->_runtimePath; + } + } + + /** + * @param string the directory that stores runtime files. + * @throws CException if the directory does not exist or is not writable + */ + public function setRuntimePath($path) + { + if(($runtimePath=realpath($path))===false || !is_dir($runtimePath) || !is_writable($runtimePath)) + throw new CException(Yii::t('yii','Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.', + array('{path}'=>$path))); + $this->_runtimePath=$runtimePath; + } + + /** + * Returns the root directory that holds all third-party extensions. + * @return string the directory that contains all extensions. Defaults to the 'extensions' directory under 'protected'. + */ + public function getExtensionPath() + { + return Yii::getPathOfAlias('ext'); + } + + /** + * @param string the directory that contains all third-party extensions. + */ + public function setExtensionPath($path) + { + if(($extensionPath=realpath($path))===false || !is_dir($extensionPath)) + throw new CException(Yii::t('yii','Extension path "{path}" does not exist.', + array('{path}'=>$path))); + Yii::setPathOfAlias('ext',$extensionPath); + } + + /** + * @return string the language that the user is using and the application should be targeted to. + * Defaults to the {@link sourceLanguage source language}. + */ + public function getLanguage() + { + return $this->_language===null ? $this->sourceLanguage : $this->_language; + } + + /** + * Specifies which language the application is targeted to. + * + * This is the language that the application displays to end users. + * If set null, it uses the {@link sourceLanguage source language}. + * + * Unless your application needs to support multiple languages, you should always + * set this language to null to maximize the application's performance. + * @param string the user language (e.g. 'en_US', 'zh_CN'). + * If it is null, the {@link sourceLanguage} will be used. + */ + public function setLanguage($language) + { + $this->_language=$language; + } + + /** + * Returns the localized version of a specified file. + * + * The searching is based on the specified language code. In particular, + * a file with the same name will be looked for under the subdirectory + * named as the locale ID. For example, given the file "path/to/view.php" + * and locale ID "zh_cn", the localized file will be looked for as + * "path/to/zh_cn/view.php". If the file is not found, the original file + * will be returned. + * + * For consistency, it is recommended that the locale ID is given + * in lower case and in the format of LanguageID_RegionID (e.g. "en_us"). + * + * @param string the original file + * @param string the language that the original file is in. If null, the application {@link sourceLanguage source language} is used. + * @param string the desired language that the file should be localized to. If null, the {@link getLanguage application language} will be used. + * @return string the matching localized file. The original file is returned if no localized version is found + * or if source language is the same as the desired language. + */ + public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null) + { + if($srcLanguage===null) + $srcLanguage=$this->sourceLanguage; + if($language===null) + $language=$this->getLanguage(); + if($language===$srcLanguage) + return $srcFile; + $desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile); + return is_file($desiredFile) ? $desiredFile : $srcFile; + } + + /** + * @param string locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used. + * @return CLocale the locale instance + */ + public function getLocale($localeID=null) + { + return CLocale::getInstance($localeID===null?$this->getLanguage():$localeID); + } + + /** + * @return CNumberFormatter the locale-dependent number formatter. + * The current {@link getLocale application locale} will be used. + */ + public function getNumberFormatter() + { + return $this->getLocale()->getNumberFormatter(); + } + + /** + * @return CDateFormatter the locale-dependent date formatter. + * The current {@link getLocale application locale} will be used. + */ + public function getDateFormatter() + { + return $this->getLocale()->getDateFormatter(); + } + + /** + * @return CDbConnection the database connection + */ + public function getDb() + { + return $this->getComponent('db'); + } + + /** + * @return CErrorHandler the error handler application component. + */ + public function getErrorHandler() + { + return $this->getComponent('errorHandler'); + } + + /** + * @return CSecurityManager the security manager application component. + */ + public function getSecurityManager() + { + return $this->getComponent('securityManager'); + } + + /** + * @return CStatePersister the state persister application component. + */ + public function getStatePersister() + { + return $this->getComponent('statePersister'); + } + + /** + * @return CCache the cache application component. Null if the component is not enabled. + */ + public function getCache() + { + return $this->getComponent('cache'); + } + + /** + * @return CPhpMessageSource the core message translations + */ + public function getCoreMessages() + { + return $this->getComponent('coreMessages'); + } + + /** + * @return CMessageSource the application message translations + */ + public function getMessages() + { + return $this->getComponent('messages'); + } + + /** + * Returns a global value. + * + * A global value is one that is persistent across users sessions and requests. + * @param string the name of the value to be returned + * @param mixed the default value. If the named global value is not found, this will be returned instead. + * @return mixed the named global value + * @see setGlobalState + */ + public function getGlobalState($key,$defaultValue=null) + { + if($this->_globalState===null) + $this->loadGlobalState(); + if(isset($this->_globalState[$key])) + return $this->_globalState[$key]; + else + return $defaultValue; + } + + /** + * Sets a global value. + * + * A global value is one that is persistent across users sessions and requests. + * Make sure that the value is serializable and unserializable. + * @param string the name of the value to be saved + * @param mixed the global value to be saved. It must be serializable. + * @param mixed the default value. If the named global value is the same as this value, it will be cleared from the current storage. + * @see getGlobalState + */ + public function setGlobalState($key,$value,$defaultValue=null) + { + if($this->_globalState===null) + $this->loadGlobalState(); + $this->_stateChanged=true; + if($value===$defaultValue) + unset($this->_globalState[$key]); + else + $this->_globalState[$key]=$value; + } + + /** + * Clears a global value. + * + * The value cleared will no longer be available in this request and the following requests. + * @param string the name of the value to be cleared + */ + public function clearGlobalState($key) + { + if($this->_globalState===null) + $this->loadGlobalState(); + if(isset($this->_globalState[$key])) + { + $this->_stateChanged=true; + unset($this->_globalState[$key]); + } + } + + /** + * Loads the global state data from persistent storage. + * @see getStatePersister + * @throws CException if the state persister is not available + */ + protected function loadGlobalState() + { + $persister=$this->getStatePersister(); + if(($this->_globalState=$persister->load())===null) + $this->_globalState=array(); + $this->_stateChanged=false; + $this->attachEventHandler('onEndRequest',array($this,'saveGlobalState')); + } + + /** + * Saves the global state data into persistent storage. + * @see getStatePersister + * @throws CException if the state persister is not available + */ + protected function saveGlobalState() + { + if($this->_stateChanged) + { + $persister=$this->getStatePersister(); + $this->_stateChanged=false; + $persister->save($this->_globalState); + } + } + + /** + * Handles uncaught PHP exceptions. + * + * This method is implemented as a PHP exception handler. It requires + * that constant YII_ENABLE_EXCEPTION_HANDLER be defined true. + * + * This method will first raise an {@link onException} event. + * If the exception is not handled by any event handler, it will call + * {@link getErrorHandler errorHandler} to process the exception. + * + * The application will be terminated by this method. + * + * @param Exception exception that is not caught + */ + public function handleException($exception) + { + // disable error capturing to avoid recursive errors + restore_error_handler(); + restore_exception_handler(); + + $category='exception.'.get_class($exception); + if($exception instanceof CHttpException) + $category.='.'.$exception->statusCode; + $message=(string)$exception; + if(isset($_SERVER['REQUEST_URI'])) + $message.=' REQUEST_URI='.$_SERVER['REQUEST_URI']; + Yii::log($message,CLogger::LEVEL_ERROR,$category); + + try + { + $event=new CExceptionEvent($this,$exception); + $this->onException($event); + if(!$event->handled) + { + // try an error handler + if(($handler=$this->getErrorHandler())!==null) + $handler->handle($event); + else + $this->displayException($exception); + } + } + catch(Exception $e) + { + $this->displayException($e); + } + $this->end(1); + } + + /** + * Handles PHP execution errors such as warnings, notices. + * + * This method is implemented as a PHP error handler. It requires + * that constant YII_ENABLE_ERROR_HANDLER be defined true. + * + * This method will first raise an {@link onError} event. + * If the error is not handled by any event handler, it will call + * {@link getErrorHandler errorHandler} to process the error. + * + * The application will be terminated by this method. + * + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + */ + public function handleError($code,$message,$file,$line) + { + if($code & error_reporting()) + { + // disable error capturing to avoid recursive errors + restore_error_handler(); + restore_exception_handler(); + + $log="$message ($file:$line)\nStack trace:\n"; + $trace=debug_backtrace(); + // skip the first 3 stacks as they do not tell the error position + if(count($trace)>3) + $trace=array_slice($trace,3); + foreach($trace as $i=>$t) + { + if(!isset($t['file'])) + $t['file']='unknown'; + if(!isset($t['line'])) + $t['line']=0; + if(!isset($t['function'])) + $t['function']='unknown'; + $log.="#$i {$t['file']}({$t['line']}): "; + if(isset($t['object']) && is_object($t['object'])) + $log.=get_class($t['object']).'->'; + $log.="{$t['function']}()\n"; + } + if(isset($_SERVER['REQUEST_URI'])) + $log.='REQUEST_URI='.$_SERVER['REQUEST_URI']; + Yii::log($log,CLogger::LEVEL_ERROR,'php'); + + try + { + $event=new CErrorEvent($this,$code,$message,$file,$line); + $this->onError($event); + if(!$event->handled) + { + // try an error handler + if(($handler=$this->getErrorHandler())!==null) + $handler->handle($event); + else + $this->displayError($code,$message,$file,$line); + } + } + catch(Exception $e) + { + $this->displayException($e); + } + $this->end(1); + } + } + + /** + * Raised when an uncaught PHP exception occurs. + * + * An event handler can set the {@link CExceptionEvent::handled handled} + * property of the event parameter to be true to indicate no further error + * handling is needed. Otherwise, the {@link getErrorHandler errorHandler} + * application component will continue processing the error. + * + * @param CExceptionEvent event parameter + */ + public function onException($event) + { + $this->raiseEvent('onException',$event); + } + + /** + * Raised when a PHP execution error occurs. + * + * An event handler can set the {@link CErrorEvent::handled handled} + * property of the event parameter to be true to indicate no further error + * handling is needed. Otherwise, the {@link getErrorHandler errorHandler} + * application component will continue processing the error. + * + * @param CErrorEvent event parameter + */ + public function onError($event) + { + $this->raiseEvent('onError',$event); + } + + /** + * Displays the captured PHP error. + * This method displays the error in HTML when there is + * no active error handler. + * @param integer error code + * @param string error message + * @param string error file + * @param string error line + */ + public function displayError($code,$message,$file,$line) + { + if(YII_DEBUG) + { + echo "<h1>PHP Error [$code]</h1>\n"; + echo "<p>$message ($file:$line)</p>\n"; + echo '<pre>'; + debug_print_backtrace(); + echo '</pre>'; + } + else + { + echo "<h1>PHP Error [$code]</h1>\n"; + echo "<p>$message</p>\n"; + } + } + + /** + * Displays the uncaught PHP exception. + * This method displays the exception in HTML when there is + * no active error handler. + * @param Exception the uncaught exception + */ + public function displayException($exception) + { + if(YII_DEBUG) + { + echo '<h1>'.get_class($exception)."</h1>\n"; + echo '<p>'.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().')</p>'; + echo '<pre>'.$exception->getTraceAsString().'</pre>'; + } + else + { + echo '<h1>'.get_class($exception)."</h1>\n"; + echo '<p>'.$exception->getMessage().'</p>'; + } + } + + /** + * Initializes the class autoloader and error handlers. + */ + protected function initSystemHandlers() + { + if(YII_ENABLE_EXCEPTION_HANDLER) + set_exception_handler(array($this,'handleException')); + if(YII_ENABLE_ERROR_HANDLER) + set_error_handler(array($this,'handleError'),error_reporting()); + } + + /** + * Registers the core application components. + * @see setComponents + */ + protected function registerCoreComponents() + { + $components=array( + 'coreMessages'=>array( + 'class'=>'CPhpMessageSource', + 'language'=>'en_us', + 'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages', + ), + 'db'=>array( + 'class'=>'CDbConnection', + ), + 'messages'=>array( + 'class'=>'CPhpMessageSource', + ), + 'errorHandler'=>array( + 'class'=>'CErrorHandler', + ), + 'securityManager'=>array( + 'class'=>'CSecurityManager', + ), + 'statePersister'=>array( + 'class'=>'CStatePersister', + ), + ); + + $this->setComponents($components); + } +} diff --git a/web/framework-1.0.8/base/CApplicationComponent.php b/web/framework-1.0.8/base/CApplicationComponent.php new file mode 100644 index 0000000..7bbe89e --- /dev/null +++ b/web/framework-1.0.8/base/CApplicationComponent.php @@ -0,0 +1,56 @@ +<?php +/** + * This file contains the base application component class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CApplicationComponent is the base class for application component classes. + * + * CApplicationComponent implements the basic methods required by {@link IApplicationComponent}. + * + * When developing an application component, try to put application component initialization code in + * the {@link init()} method instead of the constructor. This has the advantage that + * the application component can be customized through application configuration. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CApplicationComponent.php 555 2009-01-20 21:36:32Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +abstract class CApplicationComponent extends CComponent implements IApplicationComponent +{ + /** + * @var array the behaviors that should be attached to this component. + * The behaviors will be attached to the component when {@link init} is called. + * Please refer to {@link CModel::behaviors} on how to specify the value of this property. + * @since 1.0.2 + */ + public $behaviors=array(); + + private $_initialized=false; + + /** + * Initializes the application component. + * This method is required by {@link IApplicationComponent} and is invoked by application. + * If you override this method, make sure to call the parent implementation + * so that the application component can be marked as initialized. + */ + public function init() + { + $this->attachBehaviors($this->behaviors); + $this->_initialized=true; + } + + /** + * @return boolean whether this application component has been initialized (i.e., {@link init()} is invoked.) + */ + public function getIsInitialized() + { + return $this->_initialized; + } +} diff --git a/web/framework-1.0.8/base/CBehavior.php b/web/framework-1.0.8/base/CBehavior.php new file mode 100644 index 0000000..28954c5 --- /dev/null +++ b/web/framework-1.0.8/base/CBehavior.php @@ -0,0 +1,87 @@ +<?php +/** + * CBehavior class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CBehavior is a convenient base class for behavior classes. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CBehavior.php 554 2009-01-20 21:20:15Z qiang.xue $ + * @package system.base + * @since 1.0.2 + */ +class CBehavior extends CComponent implements IBehavior +{ + private $_enabled; + private $_owner; + + /** + * Declares events and the corresponding event handler methods. + * The events are defined by the {@link owner} component, while the handler + * methods by the behavior class. The handlers will be attached to the corresponding + * events when the behavior is attached to the {@link owner} component; and they + * will be detached from the events when the behavior is detached from the component. + * @return array events (array keys) and the corresponding event handler methods (array values). + */ + public function events() + { + return array(); + } + + /** + * Attaches the behavior object to the component. + * The default implementation will set the {@link owner} property + * and attach event handlers as declared in {@link events}. + * Make sure you call the parent implementation if you override this method. + * @param CComponent the component that this behavior is to be attached to. + */ + public function attach($owner) + { + $this->_owner=$owner; + foreach($this->events() as $event=>$handler) + $owner->attachEventHandler($event,array($this,$handler)); + } + + /** + * Detaches the behavior object from the component. + * The default implementation will unset the {@link owner} property + * and detach event handlers declared in {@link events}. + * Make sure you call the parent implementation if you override this method. + * @param CComponent the component that this behavior is to be detached from. + */ + public function detach($owner) + { + foreach($this->events() as $event=>$handler) + $owner->detachEventHandler($event,array($this,$handler)); + $this->_owner=null; + } + + /** + * @return CComponent the owner component that this behavior is attached to. + */ + public function getOwner() + { + return $this->_owner; + } + + /** + * @return boolean whether this behavior is enabled + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * @param boolean whether this behavior is enabled + */ + public function setEnabled($value) + { + $this->_enabled=$value; + } +} diff --git a/web/framework-1.0.8/base/CComponent.php b/web/framework-1.0.8/base/CComponent.php new file mode 100644 index 0000000..11e49bb --- /dev/null +++ b/web/framework-1.0.8/base/CComponent.php @@ -0,0 +1,599 @@ +<?php +/** + * This file contains the foundation classes for component-based and event-driven programming. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CComponent is the base class for all components. + * + * CComponent implements the protocol of defining, using properties and events. + * + * A property is defined by a getter method, and/or a setter method. + * Properties can be accessed in the way like accessing normal object members. + * Reading or writing a property will cause the invocation of the corresponding + * getter or setter method, e.g., + * <pre> + * $a=$component->text; // equivalent to $a=$component->getText(); + * $component->text='abc'; // equivalent to $component->setText('abc'); + * </pre> + * The signatures of getter and setter methods are as follows, + * <pre> + * // getter, defines a readable property 'text' + * public function getText() { ... } + * // setter, defines a writable property 'text' with $value to be set to the property + * public function setText($value) { ... } + * </pre> + * + * An event is defined by the presence of a method whose name starts with 'on'. + * The event name is the method name. When an event is raised, functions + * (called event handlers) attached to the event will be invoked automatically. + * + * An event can be raised by calling {@link raiseEvent} method, upon which + * the attached event handlers will be invoked automatically in the order they + * are attached to the event. Event handlers must have the following signature, + * <pre> + * function eventHandler($event) { ... } + * </pre> + * where $event includes parameters associated with the event. + * + * To attach an event handler to an event, see {@link attachEventHandler}. + * You can also use the following syntax: + * <pre> + * $component->onClick=$callback; // or $component->onClick->add($callback); + * </pre> + * where $callback refers to a valid PHP callback. Below we show some callback examples: + * <pre> + * 'handleOnClick' // handleOnClick() is a global function + * array($object,'handleOnClick') // using $object->handleOnClick() + * array('Page','handleOnClick') // using Page::handleOnClick() + * </pre> + * + * To raise an event, use {@link raiseEvent}. The on-method defining an event is + * commonly written like the following: + * <pre> + * public function onClick($event) + * { + * $this->raiseEvent('onClick',$event); + * } + * </pre> + * where <code>$event</code> is an instance of {@link CEvent} or its child class. + * One can then raise the event by calling the on-method instead of {@link raiseEvent} directly. + * + * Both property names and event names are case-insensitive. + * + * Starting from version 1.0.2, CComponent supports behaviors. A behavior is an + * instance of {@link IBehavior} which is attached to a component. The methods of + * the behavior can be invoked as if they belong to the component. Multiple behaviors + * can be attached to the same component. + * + * To attach a behavior to a component, call {@link attachBehavior}; and to detach the behavior + * from the component, call {@link detachBehavior}. + * + * A behavior can be temporarily enabled or disabled by calling {@link enableBehavior} + * or {@link disableBehavior}, respectively. When disabled, the behavior methods cannot + * be invoked via the component. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CComponent.php 963 2009-04-28 12:47:01Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CComponent +{ + private $_e; + private $_m; + + /** + * Returns a property value, an event handler list or a behavior based on its name. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to read a property or obtain event handlers: + * <pre> + * $value=$component->propertyName; + * $handlers=$component->eventName; + * </pre> + * @param string the property name or event name + * @return mixed the property value, event handlers attached to the event, or the named behavior (since version 1.0.2) + * @throws CException if the property or event is not defined + * @see __set + */ + public function __get($name) + { + $getter='get'.$name; + if(method_exists($this,$getter)) + return $this->$getter(); + else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) + { + // duplicating getEventHandlers() here for performance + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new CList; + return $this->_e[$name]; + } + else if(isset($this->_m[$name])) + return $this->_m[$name]; + else + throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.', + array('{class}'=>get_class($this), '{property}'=>$name))); + } + + /** + * Sets value of a component property. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to set a property or attach an event handler + * <pre> + * $this->propertyName=$value; + * $this->eventName=$callback; + * </pre> + * @param string the property name or the event name + * @param mixed the property value or callback + * @throws CException if the property/event is not defined or the property is read only. + * @see __get + */ + public function __set($name,$value) + { + $setter='set'.$name; + if(method_exists($this,$setter)) + $this->$setter($value); + else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) + { + // duplicating getEventHandlers() here for performance + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new CList; + $this->_e[$name]->add($value); + } + else if(method_exists($this,'get'.$name)) + throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.', + array('{class}'=>get_class($this), '{property}'=>$name))); + else + throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.', + array('{class}'=>get_class($this), '{property}'=>$name))); + } + + /** + * Checks if a property value is null. + * Do not call this method. This is a PHP magic method that we override + * to allow using isset() to detect if a component property is set or not. + * @param string the property name or the event name + * @since 1.0.1 + */ + public function __isset($name) + { + $getter='get'.$name; + if(method_exists($this,$getter)) + return $this->$getter()!==null; + else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) + { + $name=strtolower($name); + return isset($this->_e[$name]) && $this->_e[$name]->getCount(); + } + else + return false; + } + + /** + * Sets a component property to be null. + * Do not call this method. This is a PHP magic method that we override + * to allow using unset() to set a component property to be null. + * @param string the property name or the event name + * @throws CException if the property is read only. + * @since 1.0.1 + */ + public function __unset($name) + { + $setter='set'.$name; + if(method_exists($this,$setter)) + $this->$setter(null); + else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) + unset($this->_e[strtolower($name)]); + else if(method_exists($this,'get'.$name)) + throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.', + array('{class}'=>get_class($this), '{property}'=>$name))); + } + + /** + * Calls the named method which is not a class method. + * Do not call this method. This is a PHP magic method that we override + * to implement the behavior feature. + * @param string the method name + * @param array method parameters + * @return mixed the method return value + * @since 1.0.2 + */ + public function __call($name,$parameters) + { + if($this->_m!==null) + { + foreach($this->_m as $object) + { + if($object->enabled && method_exists($object,$name)) + return call_user_func_array(array($object,$name),$parameters); + } + } + throw new CException(Yii::t('yii','{class} does not have a method named "{name}".', + array('{class}'=>get_class($this), '{name}'=>$name))); + } + + /** + * Returns the named behavior object. + * The name 'asa' stands for 'as a'. + * @param string the behavior name + * @return IBehavior the behavior object, or null if the behavior does not exist + * @since 1.0.2 + */ + public function asa($behavior) + { + return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null; + } + + /** + * Attaches a list of behaviors to the component. + * Each behavior is indexed by its name and should be an instance of + * {@link IBehavior}, a string specifying the behavior class, or an + * array of the following structure: + * <pre> + * array( + * 'class'=>'path.to.BehaviorClass', + * 'property1'=>'value1', + * 'property2'=>'value2', + * ) + * </pre> + * @param array list of behaviors to be attached to the component + * @since 1.0.2 + */ + public function attachBehaviors($behaviors) + { + foreach($behaviors as $name=>$behavior) + $this->attachBehavior($name,$behavior); + } + + /** + * Detaches all behaviors from the component. + * @since 1.0.2 + */ + public function detachBehaviors() + { + if($this->_m!==null) + { + foreach($this->_m as $name=>$behavior) + $this->detachBehavior($name); + $this->_m=null; + } + } + + /** + * Attaches a behavior to this component. + * This method will create the behavior object based on the given + * configuration. After that, the behavior object will be initialized + * by calling its {@link IBehavior::attach} method. + * @param string the behavior's name. It should uniquely identify this behavior. + * @param mixed the behavior configuration. This is passed as the first + * parameter to {@link YiiBase::createComponent} to create the behavior object. + * @return IBehavior the behavior object + * @since 1.0.2 + */ + public function attachBehavior($name,$behavior) + { + if(!($behavior instanceof IBehavior)) + $behavior=Yii::createComponent($behavior); + $behavior->setEnabled(true); + $behavior->attach($this); + return $this->_m[$name]=$behavior; + } + + /** + * Detaches a behavior from the component. + * The behavior's {@link IBehavior::detach} method will be invoked. + * @param string the behavior's name. It uniquely identifies the behavior. + * @return IBehavior the detached behavior. Null if the behavior does not exist. + * @since 1.0.2 + */ + public function detachBehavior($name) + { + if(isset($this->_m[$name])) + { + $this->_m[$name]->detach($this); + $behavior=$this->_m[$name]; + unset($this->_m[$name]); + return $behavior; + } + } + + /** + * Enables all behaviors attached to this component. + * @since 1.0.2 + */ + public function enableBehaviors() + { + if($this->_m!==null) + { + foreach($this->_m as $behavior) + $behavior->setEnabled(true); + } + } + + /** + * Disables all behaviors attached to this component. + * @since 1.0.2 + */ + public function disableBehaviors() + { + if($this->_m!==null) + { + foreach($this->_m as $behavior) + $behavior->setEnabled(false); + } + } + + /** + * Enables an attached behavior. + * A behavior is only effective when it is enabled. + * A behavior is enabled when first attached. + * @param string the behavior's name. It uniquely identifies the behavior. + * @since 1.0.2 + */ + public function enableBehavior($name) + { + if(isset($this->_m[$name])) + $this->_m[$name]->setEnabled(true); + } + + /** + * Disables an attached behavior. + * A behavior is only effective when it is enabled. + * @param string the behavior's name. It uniquely identifies the behavior. + * @since 1.0.2 + */ + public function disableBehavior($name) + { + if(isset($this->_m[$name])) + $this->_m[$name]->setEnabled(false); + } + + /** + * Determines whether a property is defined. + * A property is defined if there is a getter or setter method + * defined in the class. Note, property names are case-insensitive. + * @param string the property name + * @return boolean whether the property is defined + * @see canGetProperty + * @see canSetProperty + */ + public function hasProperty($name) + { + return method_exists($this,'get'.$name) || method_exists($this,'set'.$name); + } + + /** + * Determines whether a property can be read. + * A property can be read if the class has a getter method + * for the property name. Note, property name is case-insensitive. + * @param string the property name + * @return boolean whether the property can be read + * @see canSetProperty + */ + public function canGetProperty($name) + { + return method_exists($this,'get'.$name); + } + + /** + * Determines whether a property can be set. + * A property can be written if the class has a setter method + * for the property name. Note, property name is case-insensitive. + * @param string the property name + * @return boolean whether the property can be written + * @see canGetProperty + */ + public function canSetProperty($name) + { + return method_exists($this,'set'.$name); + } + + /** + * Determines whether an event is defined. + * An event is defined if the class has a method named like 'onXXX'. + * Note, event name is case-insensitive. + * @param string the event name + * @return boolean whether an event is defined + */ + public function hasEvent($name) + { + return !strncasecmp($name,'on',2) && method_exists($this,$name); + } + + /** + * Checks whether the named event has attached handlers. + * @param string the event name + * @return boolean whether an event has been attached one or several handlers + */ + public function hasEventHandler($name) + { + $name=strtolower($name); + return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0; + } + + /** + * Returns the list of attached event handlers for an event. + * @param string the event name + * @return CList list of attached event handlers for the event + * @throws CException if the event is not defined + */ + public function getEventHandlers($name) + { + if($this->hasEvent($name)) + { + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new CList; + return $this->_e[$name]; + } + else + throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.', + array('{class}'=>get_class($this), '{event}'=>$name))); + } + + /** + * Attaches an event handler to an event. + * + * An event handler must be a valid PHP callback, i.e., a string referring to + * a global function name, or an array containing two elements with + * the first element being an object and the second element a method name + * of the object. + * + * An event handler must be defined with the following signature, + * <pre> + * function handlerName($event) {} + * </pre> + * where $event includes parameters associated with the event. + * + * This is a convenient method of attaching a handler to an event. + * It is equivalent to the following code: + * <pre> + * $component->getEventHandlers($eventName)->add($eventHandler); + * </pre> + * + * Using {@link getEventHandlers}, one can also specify the excution order + * of multiple handlers attaching to the same event. For example: + * <pre> + * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler); + * </pre> + * makes the handler to be invoked first. + * + * @param string the event name + * @param callback the event handler + * @throws CException if the event is not defined + * @see detachEventHandler + */ + public function attachEventHandler($name,$handler) + { + $this->getEventHandlers($name)->add($handler); + } + + /** + * Detaches an existing event handler. + * This method is the opposite of {@link attachEventHandler}. + * @param string event name + * @param callback the event handler to be removed + * @return boolean if the detachment process is successful + * @see attachEventHandler + */ + public function detachEventHandler($name,$handler) + { + if($this->hasEventHandler($name)) + return $this->getEventHandlers($name)->remove($handler)!==false; + else + return false; + } + + /** + * Raises an event. + * This method represents the happening of an event. It invokes + * all attached handlers for the event. + * @param string the event name + * @param CEvent the event parameter + * @throws CException if the event is undefined or an event handler is invalid. + */ + public function raiseEvent($name,$event) + { + $name=strtolower($name); + if(isset($this->_e[$name])) + { + foreach($this->_e[$name] as $handler) + { + if(is_string($handler)) + call_user_func($handler,$event); + else if(is_callable($handler,true)) + { + // an array: 0 - object, 1 - method name + list($object,$method)=$handler; + if(is_string($object)) // static method call + call_user_func($handler,$event); + else if(method_exists($object,$method)) + $object->$method($event); + else + throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".', + array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1]))); + } + else + throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".', + array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler)))); + // stop further handling if param.handled is set true + if(($event instanceof CEvent) && $event->handled) + return; + } + } + else if(YII_DEBUG && !$this->hasEvent($name)) + throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.', + array('{class}'=>get_class($this), '{event}'=>$name))); + } +} + + +/** + * CEvent is the base class for all event classes. + * + * It encapsulates the parameters associated with an event. + * The {@link sender} property describes who raises the event. + * And the {@link handled} property indicates if the event is handled. + * If an event handler sets {@link handled} to true, those handlers + * that are not invoked yet will not be invoked anymore. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CComponent.php 963 2009-04-28 12:47:01Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CEvent extends CComponent +{ + /** + * @var object the sender of this event + */ + public $sender; + /** + * @var boolean whether the event is handled. Defaults to false. + * When a handler sets this true, the rest uninvoked handlers will not be invoked anymore. + */ + public $handled=false; + + /** + * Constructor. + * @param mixed sender of the event + */ + public function __construct($sender=null) + { + $this->sender=$sender; + } +} + + +/** + * CEnumerable is the base class for all enumerable types. + * + * To define an enumerable type, extend CEnumberable and define string constants. + * Each constant represents an enumerable value. + * The constant name must be the same as the constant value. + * For example, + * <pre> + * class TextAlign extends CEnumerable + * { + * const Left='Left'; + * const Right='Right'; + * } + * </pre> + * Then, one can use the enumerable values such as TextAlign::Left and + * TextAlign::Right. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CComponent.php 963 2009-04-28 12:47:01Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CEnumerable +{ +} diff --git a/web/framework-1.0.8/base/CErrorEvent.php b/web/framework-1.0.8/base/CErrorEvent.php new file mode 100644 index 0000000..1b04084 --- /dev/null +++ b/web/framework-1.0.8/base/CErrorEvent.php @@ -0,0 +1,54 @@ +<?php +/** + * CErrorEvent class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CErrorEvent represents the parameter for the {@link CApplication::onError onError} event. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CErrorEvent.php 870 2009-03-22 15:06:23Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CErrorEvent extends CEvent +{ + /** + * @var string error code + */ + public $code; + /** + * @var string error message + */ + public $message; + /** + * @var string error message + */ + public $file; + /** + * @var string error file + */ + public $line; + + /** + * Constructor. + * @param mixed sender of the event + * @param string error code + * @param string error message + * @param string error file + * @param integer error line + */ + public function __construct($sender,$code,$message,$file,$line) + { + $this->code=$code; + $this->message=$message; + $this->file=$file; + $this->line=$line; + parent::__construct($sender); + } +} diff --git a/web/framework-1.0.8/base/CErrorHandler.php b/web/framework-1.0.8/base/CErrorHandler.php new file mode 100644 index 0000000..6173cd2 --- /dev/null +++ b/web/framework-1.0.8/base/CErrorHandler.php @@ -0,0 +1,336 @@ +<?php +/** + * This file contains the error handler application component. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CErrorHandler handles uncaught PHP errors and exceptions. + * + * It displays these errors using appropriate views based on the + * nature of the error and the mode the application runs at. + * It also chooses the most preferred language for displaying the error. + * + * CErrorHandler uses two sets of views: + * <ul> + * <li>development views, named as <code>exception.php</code>; + * <li>production views, named as <code>error<StatusCode>.php</code>; + * </ul> + * where <StatusCode> stands for the HTTP error code (e.g. error500.php). + * Localized views are named similarly but located under a subdirectory + * whose name is the language code (e.g. zh_cn/error500.php). + * + * Development views are displayed when the application is in debug mode + * (i.e. YII_DEBUG is defined as true). Detailed error information with source code + * are displayed in these views. Production views are meant to be shown + * to end-users and are used when the application is in production mode. + * For security reasons, they only display the error message without any + * sensitive information. + * + * CErrorHandler looks for the view templates from the following locations in order: + * <ol> + * <li><code>themes/ThemeName/views/system</code>: when a theme is active.</li> + * <li><code>protected/views/system</code></li> + * <li><code>framework/views</code></li> + * </ol> + * If the view is not found in a directory, it will be looked for in the next directory. + * + * The property {@link maxSourceLines} can be changed to specify the number + * of source code lines to be displayed in development views. + * + * CErrorHandler is a core application component that can be accessed via + * {@link CApplication::getErrorHandler()}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CErrorHandler.php 1053 2009-05-23 02:34:03Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CErrorHandler extends CApplicationComponent +{ + /** + * @var integer maximum number source code lines to be displayed. Defaults to 25. + */ + public $maxSourceLines=25; + /** + * @var string the application administrator information (could be a name or email link). It is displayed in error pages to end users. Defaults to 'the webmaster'. + */ + public $adminInfo='the webmaster'; + /** + * @var boolean whether to discard any existing page output before error display. Defaults to true. + */ + public $discardOutput=true; + /** + * @var string the route (e.g. 'site/error') to the controller action that will be used to display external errors. + * Inside the action, it can retrieve the error information by Yii::app()->errorHandler->error. + * This property defaults to null, meaning CErrorHandler will handle the error display. + * @since 1.0.6 + */ + public $errorAction; + + private $_error; + + /** + * Handles the exception/error event. + * This method is invoked by the application whenever it captures + * an exception or PHP error. + * @param CEvent the event containing the exception/error information + */ + public function handle($event) + { + // set event as handled to prevent it from being handled by other event handlers + $event->handled=true; + + if($this->discardOutput) + { + while(@ob_end_clean()) ; + } + + if($event instanceof CExceptionEvent) + $this->handleException($event->exception); + else // CErrorEvent + $this->handleError($event); + } + + /** + * Returns the details about the error that is currently being handled. + * The error is returned in terms of an array, with the following information: + * <ul> + * <li>code - the HTTP status code (e.g. 403, 500)</li> + * <li>type - the error type (e.g. 'CHttpException', 'PHP Error')</li> + * <li>message - the error message</li> + * <li>file - the name of the PHP script file where the error occurs</li> + * <li>line - the line number of the code where the error occurs</li> + * <li>trace - the call stack of the error</li> + * <li>source - the context source code where the error occurs</li> + * </ul> + * @return array the error details. Null if there is no error. + * @since 1.0.6 + */ + public function getError() + { + return $this->_error; + } + + /** + * Handles the exception. + * @param Exception the exception captured + */ + protected function handleException($exception) + { + $app=Yii::app(); + if($app instanceof CWebApplication) + { + if(($trace=$this->getExactTrace($exception))===null) + { + $fileName=$exception->getFile(); + $errorLine=$exception->getLine(); + } + else + { + $fileName=$trace['file']; + $errorLine=$trace['line']; + } + $this->_error=$data=array( + 'code'=>($exception instanceof CHttpException)?$exception->statusCode:500, + 'type'=>get_class($exception), + 'message'=>$exception->getMessage(), + 'file'=>$fileName, + 'line'=>$errorLine, + 'trace'=>$exception->getTraceAsString(), + 'source'=>$this->getSourceLines($fileName,$errorLine), + ); + + if(!headers_sent()) + header("HTTP/1.0 {$data['code']} ".get_class($exception)); + if($exception instanceof CHttpException || !YII_DEBUG) + $this->render('error',$data); + else + $this->render('exception',$data); + } + else + $app->displayException($exception); + } + + /** + * Handles the PHP error. + * @param CErrorEvent the PHP error event + */ + protected function handleError($event) + { + $trace=debug_backtrace(); + // skip the first 3 stacks as they do not tell the error position + if(count($trace)>3) + $trace=array_slice($trace,3); + $traceString=''; + foreach($trace as $i=>$t) + { + if(!isset($t['file'])) + $t['file']='unknown'; + if(!isset($t['line'])) + $t['line']=0; + if(!isset($t['function'])) + $t['function']='unknown'; + $traceString.="#$i {$t['file']}({$t['line']}): "; + if(isset($t['object']) && is_object($t['object'])) + $traceString.=get_class($t['object']).'->'; + $traceString.="{$t['function']}()\n"; + } + + $app=Yii::app(); + if($app instanceof CWebApplication) + { + $this->_error=$data=array( + 'code'=>500, + 'type'=>'PHP Error', + 'message'=>$event->message, + 'file'=>$event->file, + 'line'=>$event->line, + 'trace'=>$traceString, + 'source'=>$this->getSourceLines($event->file,$event->line), + ); + if(!headers_sent()) + header("HTTP/1.0 500 PHP Error"); + if(YII_DEBUG) + $this->render('exception',$data); + else + $this->render('error',$data); + } + else + $app->displayError($event->code,$event->message,$event->file,$event->line); + } + + /** + * @param Exception the uncaught exception + * @return array the exact trace where the problem occurs + */ + protected function getExactTrace($exception) + { + $traces=$exception->getTrace(); + $result=null; + foreach($traces as $trace) + { + // property access exception + if(isset($trace['function']) && ($trace['function']==='__get' || $trace['function']==='__set')) + return $trace; + } + return null; + } + + /** + * Renders the view. + * @param string the view name (file name without extension). + * See {@link getViewFile} for how a view file is located given its name. + * @param array data to be passed to the view + */ + protected function render($view,$data) + { + if($view==='error' && $this->errorAction!==null) + Yii::app()->runController($this->errorAction); + else + { + // additional information to be passed to view + $data['version']=$this->getVersionInfo(); + $data['time']=time(); + $data['admin']=$this->adminInfo; + include($this->getViewFile($view,$data['code'])); + } + } + + /** + * Determines which view file should be used. + * @param string view name (either 'exception' or 'error') + * @param integer HTTP status code + * @return string view file path + */ + protected function getViewFile($view,$code) + { + $viewPaths=array( + Yii::app()->getTheme()===null ? null : Yii::app()->getTheme()->getSystemViewPath(), + Yii::app() instanceof CWebApplication ? Yii::app()->getSystemViewPath() : null, + YII_PATH.DIRECTORY_SEPARATOR.'views', + ); + + foreach($viewPaths as $i=>$viewPath) + { + if($viewPath!==null) + { + $viewFile=$this->getViewFileInternal($viewPath,$view,$code,$i===2?'en_us':null); + if(is_file($viewFile)) + return $viewFile; + } + } + } + + /** + * Looks for the view under the specified directory. + * @param string the directory containing the views + * @param string view name (either 'exception' or 'error') + * @param integer HTTP status code + * @param string the language that the view file is in + * @return string view file path + */ + protected function getViewFileInternal($viewPath,$view,$code,$srcLanguage=null) + { + $app=Yii::app(); + if($view==='error') + { + $viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR."error{$code}.php",$srcLanguage); + if(!is_file($viewFile)) + $viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR.'error.php',$srcLanguage); + } + else + $viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR."exception.php",$srcLanguage); + return $viewFile; + } + + /** + * @return string server version information. If the application is in production mode, nothing is returned. + */ + protected function getVersionInfo() + { + if(YII_DEBUG) + { + $version='<a href="http://yaamp.com/">yaamp.com</a>'; + if(isset($_SERVER['SERVER_SOFTWARE'])) + $version=$_SERVER['SERVER_SOFTWARE'].' '.$version; + } + else + $version=''; + return $version; + } + + /** + * Returns the source lines around the error line. + * At most {@link maxSourceLines} lines will be returned. + * @param string source file path + * @param integer the error line number + * @return array source lines around the error line, indxed by line numbers + */ + protected function getSourceLines($file,$line) + { + // determine the max number of lines to display + $maxLines=$this->maxSourceLines; + if($maxLines<1) + $maxLines=1; + else if($maxLines>100) + $maxLines=100; + + $line--; // adjust line number to 0-based from 1-based + if($line<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$line) + return array(); + + $halfLines=(int)($maxLines/2); + $beginLine=$line-$halfLines>0?$line-$halfLines:0; + $endLine=$line+$halfLines<$lineCount?$line+$halfLines:$lineCount-1; + + $sourceLines=array(); + for($i=$beginLine;$i<=$endLine;++$i) + $sourceLines[$i+1]=$lines[$i]; + return $sourceLines; + } +} diff --git a/web/framework-1.0.8/base/CException.php b/web/framework-1.0.8/base/CException.php new file mode 100644 index 0000000..af1777d --- /dev/null +++ b/web/framework-1.0.8/base/CException.php @@ -0,0 +1,22 @@ +<?php +/** + * CException class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CException represents a generic exception for all purposes. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CException.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CException extends Exception +{ +} + diff --git a/web/framework-1.0.8/base/CExceptionEvent.php b/web/framework-1.0.8/base/CExceptionEvent.php new file mode 100644 index 0000000..512c1de --- /dev/null +++ b/web/framework-1.0.8/base/CExceptionEvent.php @@ -0,0 +1,36 @@ +<?php +/** + * CExceptionEvent class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CExceptionEvent represents the parameter for the {@link CApplication::onException onException} event. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CExceptionEvent.php 870 2009-03-22 15:06:23Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CExceptionEvent extends CEvent +{ + /** + * @var CException the exception that this event is about. + */ + public $exception; + + /** + * Constructor. + * @param mixed sender of the event + * @param CException the exception + */ + public function __construct($sender,$exception) + { + $this->exception=$exception; + parent::__construct($sender); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/base/CHttpException.php b/web/framework-1.0.8/base/CHttpException.php new file mode 100644 index 0000000..f9278fa --- /dev/null +++ b/web/framework-1.0.8/base/CHttpException.php @@ -0,0 +1,40 @@ +<?php +/** + * CHttpException class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CHttpException represents an exception caused by invalid operations of end-users. + * + * The HTTP error code can be obtained via {@link statusCode}. + * Error handlers may use this status code to decide how to format the error page. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CHttpException.php 1049 2009-05-22 20:00:35Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CHttpException extends CException +{ + /** + * @var integer HTTP status code, such as 403, 404, 500, etc. + */ + public $statusCode; + + /** + * Constructor. + * @param integer HTTP status code, such as 404, 500, etc. + * @param string error message + * @param integer error code + */ + public function __construct($status,$message=null,$code=0) + { + $this->statusCode=$status; + parent::__construct($message,$code); + } +} diff --git a/web/framework-1.0.8/base/CModel.php b/web/framework-1.0.8/base/CModel.php new file mode 100644 index 0000000..973d4d7 --- /dev/null +++ b/web/framework-1.0.8/base/CModel.php @@ -0,0 +1,634 @@ +<?php +/** + * CModel class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CModel is the base class providing the common features needed by data model objects. + * + * CModel defines the basic framework for data models that need to be validated. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CModel.php 1080 2009-05-31 20:47:16Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +abstract class CModel extends CComponent implements IteratorAggregate, ArrayAccess +{ + private $_errors=array(); // attribute name => array of errors + private $_va; // validator + private $_se=''; // scenario + + /** + * Returns the list of attribute names of the model. + * @return array list of attribute names. + * @since 1.0.1 + */ + abstract public function attributeNames(); + /** + * Returns the name of attributes that are safe to be massively assigned. + * For details about massive assignment, see the documentation of + * child classes (e.g. {@link CActiveRecord::setAttributes}, + * {@link CFormModel::setAttributes}). + * + * The returned value of this method should be in the following structure: + * <pre> + * array( + * // these attributes can be massively assigned in any scenario + * // that is not explicitly specified below + * 'attr1, attr2, ...', + * + * // these attributes can be massively assigned only in scenario 1 + * 'scenario1' => 'attr2, attr3, ...', + * + * // these attributes can be massively assigned only in scenario 2 + * 'scenario2' => 'attr1, attr3, ...', + * ); + * </pre> + * If the model is not scenario-sensitive (i.e., it is only used + * in one scenario, or all scenarios share the same set of safe attributes), + * the return value can be simplified as a single string: + * <pre> + * 'attr1, attr2, ...' + * </pre> + * @return array list of safe attribute names. + * @since 1.0.2 + */ + abstract public function safeAttributes(); + + /** + * Returns the validation rules for attributes. + * + * This method should be overridden to declare validation rules. + * Each rule is an array with the following structure: + * <pre> + * array('attribute list', 'validator name', 'on'=>'scenario name', ...validation parameters...) + * </pre> + * where + * <ul> + * <li>attribute list: specifies the attributes (separated by commas) to be validated;</li> + * <li>validator name: specifies the validator to be used. It can be the name of a model class + * method, the name of a built-in validator, or a validator class (or its path alias). + * A validation method must have the following signature: + * <pre> + * // $params refers to validation parameters given in the rule + * function validatorName($attribute,$params) + * </pre> + * A built-in validator refers to one of the validators declared in {@link CValidator::builtInValidators}. + * And a validator class is a class extending {@link CValidator}.</li> + * <li>on: this specifies the scenarios when the validation rule should be performed. + * Separate different scenarios with commas. If this option is not set, the rule + * will be applied in any scenario. Please see {@link validate} for more details about this option.</li> + * <li>additional parameters are used to initialize the corresponding validator properties. + * Please refer to inidividal validator class API for possible properties.</li> + * </ul> + * + * The following are some examples: + * <pre> + * array( + * array('username', 'required'), + * array('username', 'length', 'min'=>3, 'max'=>12), + * array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'), + * array('password', 'authenticate', 'on'=>'login'), + * ); + * </pre> + * + * Note, in order to inherit rules defined in the parent class, a child class needs to + * merge the parent rules with child rules using functions like array_merge(). + * + * @return array validation rules to be applied when {@link validate()} is called. + * @see scenario + */ + public function rules() + { + return array(); + } + + /** + * Returns a list of behaviors that this model should behave as. + * The return value should be an array of behavior configurations indexed by + * behavior names. Each behavior configuration can be either a string specifying + * the behavior class or an array of the following structure: + * <pre> + * 'behaviorName'=>array( + * 'class'=>'path.to.BehaviorClass', + * 'property1'=>'value1', + * 'property2'=>'value2', + * ) + * </pre> + * + * Note, the behavior classes must implement {@link IBehavior} or extend from + * {@link CBehavior}. Behaviors declared in this method will be attached + * to the model when it is instantiated. + * + * For more details about behaviors, see {@link CComponent}. + * @return array the behavior configurations (behavior name=>behavior configuration) + * @since 1.0.2 + */ + public function behaviors() + { + return array(); + } + + /** + * Returns the attribute labels. + * Attribute labels are mainly used in error messages of validation. + * By default an attribute label is generated using {@link generateAttributeLabel}. + * This method allows you to explicitly specify attribute labels. + * + * Note, in order to inherit labels defined in the parent class, a child class needs to + * merge the parent labels with child labels using functions like array_merge(). + * + * @return array attribute labels (name=>label) + * @see generateAttributeLabel + */ + public function attributeLabels() + { + return array(); + } + + /** + * Performs the validation. + * This method executes the validation rule as declared in {@link rules}. + * Errors found during the validation can be retrieved via {@link getErrors}. + * @param string the scenario that the validation rules should be applied. + * Defaults to empty string, meaning only those validation rules whose "on" + * option is empty will be applied. If this is a non-empty string, only + * the validation rules whose "on" option is empty or contains the specified scenario + * will be applied. As of version 1.0.4, this parameter is deprecated, and + * the {@link scenario} property is the preferred way of specifying the scenario. + * In particular, when this parameter is empty, the {@link scenario} property value + * will be used as the effective scenario. + * @param array list of attributes that should be validated. Defaults to null, + * meaning any attribute listed in the applicable validation rules should be + * validated. If this parameter is given as a list of attributes, only + * the listed attributes will be validated. + * @return boolean whether the validation is successful without any error. + * @see beforeValidate + * @see afterValidate + */ + public function validate($scenario='',$attributes=null) + { + if($scenario==='') + $scenario=$this->getScenario(); + + $this->clearErrors(); + if($this->beforeValidate($scenario)) + { + foreach($this->getValidators() as $validator) + { + if($validator->applyTo($scenario)) + $validator->validate($this,$attributes); + } + $this->afterValidate($scenario); + return !$this->hasErrors(); + } + else + return false; + } + + /** + * This method is invoked before validation starts. + * The default implementation calls {@link onBeforeValidate} to raise an event. + * You may override this method to do preliminary checks before validation. + * Make sure the parent implementation is invoked so that the event can be raised. + * @param string the set of the validation rules that should be applied. See {@link validate} + * for more details about this parameter. + * NOTE: this parameter has been available since version 1.0.1. + * @return boolean whether validation should be executed. Defaults to true. + */ + protected function beforeValidate($scenario) + { + $event=new CModelEvent($this); + $this->onBeforeValidate($event); + return $event->isValid; + } + + /** + * This method is invoked after validation ends. + * The default implementation calls {@link onAfterValidate} to raise an event. + * You may override this method to do postprocessing after validation. + * Make sure the parent implementation is invoked so that the event can be raised. + * @param string the set of the validation rules that should be applied. See {@link validate} + * for more details about this parameter. + * NOTE: this parameter has been available since version 1.0.1. + */ + protected function afterValidate($scenario) + { + $this->onAfterValidate(new CEvent($this)); + } + + /** + * This event is raised before the validation is performed. + * @param CModelEvent the event parameter + * @since 1.0.2 + */ + public function onBeforeValidate($event) + { + $this->raiseEvent('onBeforeValidate',$event); + } + + /** + * This event is raised after the validation is performed. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onAfterValidate($event) + { + $this->raiseEvent('onAfterValidate',$event); + } + + /** + * @return array list of validators created according to {@link rules}. + * @since 1.0.1 + */ + public function getValidators() + { + return $this->createValidators(); + } + + /** + * Returns the validators that are applied to the specified attribute under the specified scenario. + * @param string the attribute name + * @param string the scenario name. As of version 1.0.4, this parameter is deprecated, and + * the {@link scenario} property is the preferred way of specifying the scenario. + * In particular, when this parameter is empty, the {@link scenario} property value + * will be used as the effective scenario. + * @return array the validators for the attribute. An empty array is returned if no validator applies to the attribute. + * @since 1.0.2 + */ + public function getValidatorsForAttribute($attribute,$scenario='') + { + if($scenario==='') + $scenario=$this->getScenario(); + + if($this->_va===null) + { + $this->_va=array(); + foreach($this->getValidators() as $validator) + { + foreach($validator->attributes as $att) + $this->_va[$att][]=$validator; + } + } + $validators=array(); + if(isset($this->_va[$attribute])) + { + foreach($this->_va[$attribute] as $validator) + { + if($validator->applyTo($scenario)) + $validators[]=$validator; + } + } + return $validators; + } + + /** + * Returns a value indicating whether the attribute is required. + * This is determined based on the validation rules declared in {@link rules}. + * @param string attribute name + * @param string validation scenario. As of version 1.0.4, this parameter is deprecated, and + * the {@link scenario} property is the preferred way of specifying the scenario. + * In particular, when this parameter is empty, the {@link scenario} property value + * will be used as the effective scenario. + * @return boolean whether the attribute is required + * @since 1.0.2 + */ + public function isAttributeRequired($attribute,$scenario='') + { + $validators=$this->getValidatorsForAttribute($attribute,$scenario); + foreach($validators as $validator) + { + if($validator instanceof CRequiredValidator) + return true; + } + return false; + } + + /** + * Returns the text label for the specified attribute. + * @param string the attribute name + * @return string the attribute label + * @see generateAttributeLabel + * @see attributeLabels + */ + public function getAttributeLabel($attribute) + { + $labels=$this->attributeLabels(); + if(isset($labels[$attribute])) + return $labels[$attribute]; + else + return $this->generateAttributeLabel($attribute); + } + + /** + * Returns a value indicating whether there is any validation error. + * @param string attribute name. Use null to check all attributes. + * @return boolean whether there is any error. + */ + public function hasErrors($attribute=null) + { + if($attribute===null) + return $this->_errors!==array(); + else + return isset($this->_errors[$attribute]); + } + + /** + * Returns the errors for all attribute or a single attribute. + * @param string attribute name. Use null to retrieve errors for all attributes. + * @return array errors for all attributes or the specified attribute. Empty array is returned if no error. + */ + public function getErrors($attribute=null) + { + if($attribute===null) + return $this->_errors; + else + return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array(); + } + + /** + * Returns the first error of the specified attribute. + * @param string attribute name. + * @return string the error message. Null is returned if no error. + * @since 1.0.2 + */ + public function getError($attribute) + { + return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; + } + + /** + * Adds a new error to the specified attribute. + * @param string attribute name + * @param string new error message + */ + public function addError($attribute,$error) + { + $this->_errors[$attribute][]=$error; + } + + /** + * Adds a list of errors. + * @param array a list of errors. The array keys must be attribute names. + * The array values should be error messages. If an attribute has multiple errors, + * these errors must be given in terms of an array. + * You may use the result of {@link getErrors} as the value for this parameter. + * @since 1.0.5 + */ + public function addErrors($errors) + { + foreach($errors as $attribute=>$error) + { + if(is_array($error)) + { + foreach($error as $e) + $this->_errors[$attribute][]=$e; + } + else + $this->_errors[$attribute][]=$error; + } + } + + /** + * Removes errors for all attributes or a single attribute. + * @param string attribute name. Use null to remove errors for all attribute. + */ + public function clearErrors($attribute=null) + { + if($attribute===null) + $this->_errors=array(); + else + unset($this->_errors[$attribute]); + } + + /** + * @return array validators built based on {@link rules()}. + */ + public function createValidators() + { + $validators=array(); + foreach($this->rules() as $rule) + { + // debuglog($rule); + if(isset($rule[0],$rule[1])) // attributes, validator name + $validators[]=CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)); + else + throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', + array('{class}'=>get_class($this)))); + } + + // debuglog(" good"); + return $validators; + } + + /** + * Generates a user friendly attribute label. + * This is done by replacing underscores or dashes with blanks and + * changing the first letter of each word to upper case. + * For example, 'department_name' or 'DepartmentName' becomes 'Department Name'. + * @param string the column name + * @return string the attribute label + */ + public function generateAttributeLabel($name) + { + return ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name))))); + } + + /** + * Returns all attribute values. + * @param array list of attributes whose value needs to be returned. + * Defaults to null, meaning all attributes as listed in {@link attributeNames} will be returned. + * If it is an array, only the attributes in the array will be returned. + * @return array attribute values (name=>value). + */ + public function getAttributes($names=null) + { + $values=array(); + foreach($this->attributeNames() as $name) + $values[$name]=$this->$name; + + if(is_array($names)) + { + $values2=array(); + foreach($names as $name) + $values2[$name]=isset($values[$name]) ? $values[$name] : null; + return $values2; + } + else + return $values; + } + + /** + * Sets the attribute values in a massive way. + * Only safe attributes will be assigned by this method. + * + * Given a scenario, this method will assign values to the attributes + * that appear in the list returned by {@link safeAttributes} for the specified scenario: + * <ul> + * <li>If the scenario is false, attributes specified by {@link attributeNames} + * will be assigned.</li> + * <li>If the scenario is not an empty string, and it is found + * as a key in {@link safeAttributes}, the corresponding attributes + * will be assigned;</li> + * <li>If the scenario is an empty string, or if it is not found + * in {@link safeAttributes}, the first set of attributes in {@link safeAttributes} + * will be assigned.</li> + * </ul> + * @param array attribute values (name=>value) to be set. + * @param mixed scenario name. + * Note that as of version 1.0.4, if this parameter is an empty string, it will + * take the {@link scenario} property value. + * @see getSafeAttributeNames + */ + public function setAttributes($values,$scenario='') + { + if($scenario==='') + $scenario=$this->getScenario(); + + if(is_array($values)) + { + $attributes=array_flip($this->getSafeAttributeNames($scenario)); + foreach($values as $name=>$value) + { + if(isset($attributes[$name])) + $this->$name=$value; + } + } + } + + /** + * Returns the scenario that this model is in. + * Scenario affects how massive attribute assignment is carried and which + * validations should be performed. An attribute can be declared as safe + * for massive assignment and requiring validation under certain scenarios. + * @return string the scenario that this model is in. Defaults to empty. + * @since 1.0.4 + */ + public function getScenario() + { + return $this->_se; + } + + /** + * @param string the scenario that this model is in. + * @see getScenario + * @since 1.0.4 + */ + public function setScenario($value) + { + $this->_se=$value; + } + + /** + * Returns the attribute names that are safe to be massively assigned. + * This method is internally used by {@link setAttributes}. + * + * Given a scenario, this method will choose the folllowing result + * from the list returned by {@link safeAttributes}: + * <ul> + * <li>If the scenario is false, attributes specified by {@link attributeNames} + * will be returned.</li> + * <li>If the scenario is not an empty string, and it is found + * as a key in {@link safeAttributes}, the corresponding attributes + * will be returned;</li> + * <li>If the scenario is an empty string, or if it is not found + * in {@link safeAttributes}, the first set of attributes in {@link safeAttributes} + * will be returned.</li> + * </ul> + * @param string scenario name. + * Note that as of version 1.0.4, if this parameter is an empty string, it will + * take the {@link scenario} property value. + * @return array safe attribute names + * @since 1.0.2 + */ + public function getSafeAttributeNames($scenario='') + { + if($scenario==='') + $scenario=$this->getScenario(); + + if($scenario===false) + return $this->attributeNames(); + + $attributes=$this->safeAttributes(); + if(!is_array($attributes)) + $attributes=array($attributes); + + if($scenario!=='' && isset($attributes[$scenario])) + return $this->ensureArray($attributes[$scenario]); + + if(isset($attributes[0], $attributes[1])) + return $attributes; + else + return isset($attributes[0]) ? $this->ensureArray($attributes[0]) : array(); + } + + /** + * Returns an iterator for traversing the attributes in the model. + * This method is required by the interface IteratorAggregate. + * @return CMapIterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + $attributes=$this->getAttributes(); + return new CMapIterator($attributes); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + * @since 1.0.2 + */ + public function offsetExists($offset) + { + return property_exists($this,$offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + * @since 1.0.2 + */ + public function offsetGet($offset) + { + return $this->$offset; + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + * @since 1.0.2 + */ + public function offsetSet($offset,$item) + { + $this->$offset=$item; + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + * @since 1.0.2 + */ + public function offsetUnset($offset) + { + unset($this->$offset); + } + + private function ensureArray($value) + { + return is_array($value) ? $value : preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); + } +} diff --git a/web/framework-1.0.8/base/CModelBehavior.php b/web/framework-1.0.8/base/CModelBehavior.php new file mode 100644 index 0000000..89cfd16 --- /dev/null +++ b/web/framework-1.0.8/base/CModelBehavior.php @@ -0,0 +1,54 @@ +<?php +/** + * CModelBehavior class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CModelBehavior is a base class for behaviors that are attached to a model component. + * The model should extend from {@link CModel} or its child classes. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CModelBehavior.php 1185 2009-06-28 13:06:00Z qiang.xue $ + * @package system.base + * @since 1.0.2 + */ +class CModelBehavior extends CBehavior +{ + /** + * Declares events and the corresponding event handler methods. + * The default implementation returns 'onBeforeValidate' and 'onAfterValidate' events and handlers. + * If you override this method, make sure you merge the parent result to the return value. + * @return array events (array keys) and the corresponding event handler methods (array values). + * @see CBehavior::events + */ + public function events() + { + return array( + 'onBeforeValidate'=>'beforeValidate', + 'onAfterValidate'=>'afterValidate', + ); + } + + /** + * Responds to {@link CModel::onBeforeValidate} event. + * Overrides this method if you want to handle the corresponding event of the {@link owner}. + * You may set {@link CModelEvent::isValid} to be false to quit the validation process. + * @param CModelEvent event parameter + */ + public function beforeValidate($event) + { + } + + /** + * Responds to {@link CModel::onAfterValidate} event. + * Overrides this method if you want to handle the corresponding event of the {@link owner}. + * @param CEvent event parameter + */ + public function afterValidate($event) + { + } +} diff --git a/web/framework-1.0.8/base/CModelEvent.php b/web/framework-1.0.8/base/CModelEvent.php new file mode 100644 index 0000000..4463acb --- /dev/null +++ b/web/framework-1.0.8/base/CModelEvent.php @@ -0,0 +1,32 @@ +<?php +/** + * CModelEvent class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CModelEvent class. + * + * CModelEvent represents the event parameters needed by events raised by a model. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CModelEvent.php 1185 2009-06-28 13:06:00Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CModelEvent extends CEvent +{ + /** + * @var boolean whether the model is in valid status and should continue its normal method execution cycles. Defaults to true. + * For example, when this event is raised in a {@link CFormModel} object when executing {@link CModel::beforeValidate}, + * if this property is false by the event handler, the {@link CModel::validate} method will quit after handling this event. + * If true, the normal execution cycles will continue, including performing the real validations and calling + * {@link CModel::afterValidate}. + */ + public $isValid=true; +} diff --git a/web/framework-1.0.8/base/CModule.php b/web/framework-1.0.8/base/CModule.php new file mode 100644 index 0000000..89d0c9e --- /dev/null +++ b/web/framework-1.0.8/base/CModule.php @@ -0,0 +1,465 @@ +<?php +/** + * CModule class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CModule is the base class for module and application classes. + * + * CModule mainly manages application components and sub-modules. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CModule.php 1091 2009-06-05 00:39:02Z qiang.xue $ + * @package system.base + * @since 1.0.4 + */ +abstract class CModule extends CComponent +{ + /** + * @var array the IDs of the application components that should be preloaded. + */ + public $preload=array(); + /** + * @var array the behaviors that should be attached to the module. + * The behaviors will be attached to the module when {@link init} is called. + * Please refer to {@link CModel::behaviors} on how to specify the value of this property. + */ + public $behaviors=array(); + + private $_id; + private $_parentModule; + private $_basePath; + private $_modulePath; + private $_params; + private $_modules=array(); + private $_moduleConfig=array(); + private $_components=array(); + private $_componentConfig=array(); + + + /** + * Constructor. + * @param string the ID of this module + * @param CModule the parent module (if any) + * @param mixed the module configuration. It can be either an array or + * the path of a PHP file returning the configuration array. + */ + public function __construct($id,$parent,$config=null) + { + $this->_id=$id; + $this->_parentModule=$parent; + + // set basePath at early as possible to avoid trouble + if(is_string($config)) + $config=require($config); + if(isset($config['basePath'])) + { + $this->setBasePath($config['basePath']); + unset($config['basePath']); + } + else + { + $class=new ReflectionClass(get_class($this)); + $this->setBasePath(dirname($class->getFileName())); + } + Yii::setPathOfAlias($id,$this->getBasePath()); + + $this->preinit(); + + $this->configure($config); + $this->attachBehaviors($this->behaviors); + $this->preloadComponents(); + + $this->init(); + } + + /** + * Getter magic method. + * This method is overridden to support accessing application components + * like reading module properties. + * @param string application component or property name + * @return mixed the named property value + */ + public function __get($name) + { + if($this->hasComponent($name)) + return $this->getComponent($name); + else + return parent::__get($name); + } + + /** + * Checks if a property value is null. + * This method overrides the parent implementation by checking + * if the named application component is loaded. + * @param string the property name or the event name + * @return boolean whether the property value is null + */ + public function __isset($name) + { + if($this->hasComponent($name)) + return $this->getComponent($name)!==null; + else + return parent::__isset($name); + } + + /** + * @return string the module ID. + */ + public function getId() + { + return $this->_id; + } + + /** + * @param string the module ID + */ + public function setId($id) + { + $this->_id=$id; + } + + /** + * @return string the root directory of the module. Defaults to the directory containing the module class. + */ + public function getBasePath() + { + if($this->_basePath===null) + { + $class=new ReflectionClass(get_class($this)); + $this->_basePath=dirname($class->getFileName()); + } + return $this->_basePath; + } + + /** + * Sets the root directory of the module. + * This method can only be invoked at the beginning of the constructor. + * @param string the root directory of the module. + * @throws CException if the directory does not exist. + */ + public function setBasePath($path) + { + if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath)) + throw new CException(Yii::t('yii','Base path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * @return CAttributeCollection the list of user-defined parameters + */ + public function getParams() + { + if($this->_params!==null) + return $this->_params; + else + return $this->_params=new CAttributeCollection; + } + + /** + * @param array user-defined parameters. This should be in name-value pairs. + */ + public function setParams($value) + { + $params=$this->getParams(); + foreach($value as $k=>$v) + $params->add($k,$v); + } + + /** + * @return string the directory that contains the application modules. Defaults to the 'modules' subdirectory of {@link basePath}. + */ + public function getModulePath() + { + if($this->_modulePath!==null) + return $this->_modulePath; + else + return $this->_modulePath=$this->getBasePath().DIRECTORY_SEPARATOR.'modules'; + } + + /** + * @param string the directory that contains the application modules. + * @throws CException if the directory is invalid + */ + public function setModulePath($value) + { + if(($this->_modulePath=realpath($value))===false || !is_dir($this->_modulePath)) + throw new CException(Yii::t('yii','The module path "{path}" is not a valid directory.', + array('{path}'=>$value))); + } + + /** + * Sets the aliases that are used in the module. + * @param array list of aliases to be imported + */ + public function setImport($aliases) + { + foreach($aliases as $alias) + Yii::import($alias); + } + + /** + * Defines the root aliases. + * @param array list of aliases to be defined. The array keys are root aliases, + * while the array values are paths or aliases corresponding to the root aliases. + * For example, + * <pre> + * array( + * 'models'=>'application.models', // an existing alias + * 'extensions'=>'application.extensions', // an existing alias + * 'backend'=>dirname(__FILE__).'/../backend', // a directory + * ) + * </pre> + * @since 1.0.5 + */ + public function setAliases($mappings) + { + foreach($mappings as $name=>$alias) + { + if(($path=Yii::getPathOfAlias($alias))!==false) + Yii::setPathOfAlias($name,$path); + else + Yii::setPathOfAlias($name,$alias); + } + } + + /** + * @return CModule the parent module. Null if this module does not have a parent. + */ + public function getParentModule() + { + return $this->_parentModule; + } + + /** + * Retrieves the named application module. + * The module has to be declared in {@link modules}. A new instance will be created + * when calling this method with the given ID for the first time. + * @param string application module ID (case-sensitive) + * @return CModule the module instance, null if the module is disabled or does not exist. + */ + public function getModule($id) + { + if(array_key_exists($id,$this->_modules)) + return $this->_modules[$id]; + else if(isset($this->_moduleConfig[$id])) + { + $config=$this->_moduleConfig[$id]; + if(!isset($config['enabled']) || $config['enabled']) + { + Yii::trace("Loading \"$id\" module",'system.base.CModule'); + $class=$config['class']; + unset($config['class'], $config['enabled']); + if($this===Yii::app()) + $module=Yii::createComponent($class,$id,null,$config); + else + $module=Yii::createComponent($class,$this->getId().'/'.$id,$this,$config); + return $this->_modules[$id]=$module; + } + } + } + + /** + * @return array the configuration of the currently installed modules (module ID => configuration) + */ + public function getModules() + { + return $this->_moduleConfig; + } + + /** + * Configures the sub-modules of this module. + * + * Call this method to declare sub-modules and configure them with their initial property values. + * The parameter should be an array of module configurations. Each array element represents a single module, + * which can be either a string representing the module ID or an ID-configuration pair representing + * a module with the specified ID and the initial property values. + * + * For example, the following array declares two modules: + * <pre> + * array( + * 'admin', // a single module ID + * 'payment'=>array( // ID-configuration pair + * 'server'=>'paymentserver.com', + * ), + * ) + * </pre> + * + * By default, the module class is determined using the expression <code>ucfirst($moduleID).'Module'</code>. + * And the class file is located under <code>modules/$moduleID</code>. + * You may override this default by explicitly specifying the 'class' option in the configuration. + * + * You may also enable or disable a module by specifying the 'enabled' option in the configuration. + * + * @param array module configurations. + */ + public function setModules($modules) + { + foreach($modules as $id=>$module) + { + if(is_int($id)) + { + $id=$module; + $module=array(); + } + if(!isset($module['class'])) + { + Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); + $module['class']=$id.'.'.ucfirst($id).'Module'; + } + + if(isset($this->_moduleConfig[$id])) + $this->_moduleConfig[$id]=CMap::mergeArray($this->_moduleConfig[$id],$module); + else + $this->_moduleConfig[$id]=$module; + } + } + + /** + * @param string application component ID + * @return boolean whether the named application component exists (including both loaded and disabled.) + */ + public function hasComponent($id) + { + return isset($this->_components[$id]) || isset($this->_componentConfig[$id]); + } + + /** + * Retrieves the named application component. + * @param string application component ID (case-sensitive) + * @param boolean whether to create the component if it doesn't exist yet. This parameter + * has been available since version 1.0.6. + * @return IApplicationComponent the application component instance, null if the application component is disabled or does not exist. + * @see hasComponent + */ + public function getComponent($id,$createIfNull=true) + { + if(isset($this->_components[$id])) + return $this->_components[$id]; + else if(isset($this->_componentConfig[$id]) && $createIfNull) + { + $config=$this->_componentConfig[$id]; + unset($this->_componentConfig[$id]); + if(!isset($config['enabled']) || $config['enabled']) + { + Yii::trace("Loading \"$id\" application component",'system.web.CModule'); + unset($config['enabled']); + $component=Yii::createComponent($config); + $component->init(); + return $this->_components[$id]=$component; + } + } + } + + /** + * Puts a component under the management of the module. + * The component will be initialized (by calling its {@link CApplicationComponent::init() init()} + * method if it has not done so. + * @param string component ID + * @param IApplicationComponent the component + */ + public function setComponent($id,$component) + { + $this->_components[$id]=$component; + if(!$component->getIsInitialized()) + $component->init(); + } + + /** + * @return array the currently loaded components (indexed by their IDs) + */ + public function getComponents() + { + return $this->_components; + } + + /** + * Sets the application components. + * + * When a configuration is used to specify a component, it should consist of + * the component's initial property values (name-value pairs). Additionally, + * a component can be enabled (default) or disabled by specifying the 'enabled' value + * in the configuration. + * + * If a configuration is specified with an ID that is the same as an existing + * component or configuration, the existing one will be replaced silently. + * + * The following is the configuration for two components: + * <pre> + * array( + * 'db'=>array( + * 'class'=>'CDbConnection', + * 'connectionString'=>'sqlite:path/to/file.db', + * ), + * 'cache'=>array( + * 'class'=>'CDbCache', + * 'connectionID'=>'db', + * 'enabled'=>!YII_DEBUG, // enable caching in non-debug mode + * ), + * ) + * </pre> + * + * @param array application components(id=>component configuration or instances) + */ + public function setComponents($components) + { + foreach($components as $id=>$component) + { + if($component instanceof IApplicationComponent) + $this->setComponent($id,$component); + else if(isset($this->_componentConfig[$id])) + $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component); + else + $this->_componentConfig[$id]=$component; + } + } + + /** + * Configures the module with the specified configuration. + * @param array the configuration array + */ + public function configure($config) + { + if(is_array($config)) + { + foreach($config as $key=>$value) + $this->$key=$value; + } + } + + /** + * Loads static application components. + */ + protected function preloadComponents() + { + foreach($this->preload as $id) + $this->getComponent($id); + } + + /** + * Preinitializes the module. + * This method is called at the beginning of the module constructor. + * You may override this method to do some customized preinitialization work. + * Note that at this moment, the module is not configured yet. + * @see init + */ + protected function preinit() + { + } + + /** + * Initializes the module. + * This method is called at the end of the module constructor. + * Note that at this moment, the module has been configured, the behaviors + * have been attached and the application components have been registered. + * @see preinit + */ + protected function init() + { + } +} diff --git a/web/framework-1.0.8/base/CSecurityManager.php b/web/framework-1.0.8/base/CSecurityManager.php new file mode 100644 index 0000000..fb91367 --- /dev/null +++ b/web/framework-1.0.8/base/CSecurityManager.php @@ -0,0 +1,245 @@ +<?php +/** + * This file contains classes implementing security manager feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CSecurityManager provides private keys, hashing and encryption functions. + * + * CSecurityManager is used by Yii components and applications for security-related purpose. + * For example, it is used in cookie validation feature to prevent cookie data + * from being tampered. + * + * CSecurityManager is mainly used to protect data from being tampered and viewed. + * It can generate HMAC and encrypt the data. The private key used to generate HMAC + * is set by {@link setValidationKey ValidationKey}. The key used to encrypt data is + * specified by {@link setEncryptionKey EncryptionKey}. If the above keys are not + * explicitly set, random keys will be generated and used. + * + * To protected data with HMAC, call {@link hashData()}; and to check if the data + * is tampered, call {@link validateData()}, which will return the real data if + * it is not tampered. The algorithm used to generated HMAC is specified by + * {@link setValidation Validation}. + * + * To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()} + * respectively, which uses 3DES encryption algorithm. Note, the PHP Mcrypt + * extension must be installed and loaded. + * + * CSecurityManager is a core application component that can be accessed via + * {@link CApplication::getSecurityManager()}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CSecurityManager.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CSecurityManager extends CApplicationComponent +{ + const STATE_VALIDATION_KEY='Yii.CSecurityManager.validationkey'; + const STATE_ENCRYPTION_KEY='Yii.CSecurityManager.encryptionkey'; + + private $_validationKey; + private $_encryptionKey; + private $_validation='SHA1'; + + /** + * @return string a randomly generated key + */ + protected function generateRandomKey() + { + return rand().rand().rand().rand(); + } + + /** + * @return string the private key used to generate HMAC. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getValidationKey() + { + if($this->_validationKey!==null) + return $this->_validationKey; + else + { + if(($key=Yii::app()->getGlobalState(self::STATE_VALIDATION_KEY))!==null) + $this->setValidationKey($key); + else + { + $key=$this->generateRandomKey(); + $this->setValidationKey($key); + Yii::app()->setGlobalState(self::STATE_VALIDATION_KEY,$key); + } + return $this->_validationKey; + } + } + + /** + * @param string the key used to generate HMAC + * @throws CException if the key is empty + */ + public function setValidationKey($value) + { + if(!empty($value)) + $this->_validationKey=$value; + else + throw new CException(Yii::t('yii','CSecurityManager.validationKey cannot be empty.')); + } + + /** + * @return string the private key used to encrypt/decrypt data. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getEncryptionKey() + { + if($this->_encryptionKey!==null) + return $this->_encryptionKey; + else + { + if(($key=Yii::app()->getGlobalState(self::STATE_ENCRYPTION_KEY))!==null) + $this->setEncryptionKey($key); + else + { + $key=$this->generateRandomKey(); + $this->setEncryptionKey($key); + Yii::app()->setGlobalState(self::STATE_ENCRYPTION_KEY,$key); + } + return $this->_encryptionKey; + } + } + + /** + * @param string the key used to encrypt/decrypt data. + * @throws CException if the key is empty + */ + public function setEncryptionKey($value) + { + if(!empty($value)) + $this->_encryptionKey=$value; + else + throw new CException(Yii::t('yii','CSecurityManager.encryptionKey cannot be empty.')); + } + + /** + * @return string hashing algorithm used to generate HMAC. Defaults to 'SHA1'. + */ + public function getValidation() + { + return $this->_validation; + } + + /** + * @param string hashing algorithm used to generate HMAC. It must be either 'MD5' or 'SHA1'. + */ + public function setValidation($value) + { + if($value==='MD5' || $value==='SHA1') + $this->_validation=$value; + else + throw new CException(Yii::t('yii','CSecurityManager.validation must be either "MD5" or "SHA1".')); + } + + /** + * Encrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be encrypted. + * @return string the encrypted data + * @throws CException if PHP Mcrypt extension is not loaded + */ + public function encrypt($data) + { + if(extension_loaded('mcrypt')) + { + $module=mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, ''); + $key=substr(md5($this->getEncryptionKey()),0,mcrypt_enc_get_key_size($module)); + srand(); + $iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); + mcrypt_generic_init($module,$key,$iv); + $encrypted=$iv.mcrypt_generic($module,$data); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return $encrypted; + } + else + throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.')); + } + + /** + * Decrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be decrypted. + * @return string the decrypted data + * @throws CException if PHP Mcrypt extension is not loaded + */ + public function decrypt($data) + { + if(extension_loaded('mcrypt')) + { + $module=mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, ''); + $key=substr(md5($this->getEncryptionKey()),0,mcrypt_enc_get_key_size($module)); + $ivSize=mcrypt_enc_get_iv_size($module); + $iv=substr($data,0,$ivSize); + mcrypt_generic_init($module,$key,$iv); + $decrypted=mdecrypt_generic($module,substr($data,$ivSize)); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return rtrim($decrypted,"\0"); + } + else + throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.')); + } + + /** + * Prefixes data with an HMAC. + * @param string data to be hashed. + * @return string data prefixed with HMAC + */ + public function hashData($data) + { + $hmac=$this->computeHMAC($data); + return $hmac.$data; + } + + /** + * Validates if data is tampered. + * @param string data to be validated. The data must be previously + * generated using {@link hashData()}. + * @return string the real data with HMAC stripped off. False if the data + * is tampered. + */ + public function validateData($data) + { + $len=$this->_validation==='SHA1'?40:32; + if(strlen($data)>=$len) + { + $hmac=substr($data,0,$len); + $data2=substr($data,$len); + return $hmac===$this->computeHMAC($data2)?$data2:false; + } + else + return false; + } + + /** + * Computes the HMAC for the data with {@link getValidationKey ValidationKey}. + * @param string data to be generated HMAC + * @return string the HMAC for the data + */ + protected function computeHMAC($data) + { + if($this->_validation==='SHA1') + { + $pack='H40'; + $func='sha1'; + } + else + { + $pack='H32'; + $func='md5'; + } + $key=$this->getValidationKey(); + $key=str_pad($func($key), 64, chr(0)); + return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data))); + } +} diff --git a/web/framework-1.0.8/base/CStatePersister.php b/web/framework-1.0.8/base/CStatePersister.php new file mode 100644 index 0000000..ed18a22 --- /dev/null +++ b/web/framework-1.0.8/base/CStatePersister.php @@ -0,0 +1,102 @@ +<?php +/** + * This file contains classes implementing security manager feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CStatePersister implements a file-based persistent data storage. + * + * It can be used to keep data available through multiple requests and sessions. + * + * By default, CStatePersister stores data in a file named 'state.bin' that is located + * under the application {@link CApplication::getRuntimePath runtime path}. + * You may change the location by setting the {@link stateFile} property. + * + * To retrieve the data from CStatePersister, call {@link load()}. To save the data, + * call {@link save()}. + * + * Comparison among state persister, session and cache is as follows: + * <ul> + * <li>session: data persisting within a single user session.</li> + * <li>state persister: data persisting through all requests/sessions (e.g. hit counter).</li> + * <li>cache: volatile and fast storage. It may be used as storage medium for session or state persister.</li> + * </ul> + * + * Since server resource is often limited, be cautious if you plan to use CStatePersister + * to store large amount of data. You should also consider using database-based persister + * to improve the throughput. + * + * CStatePersister is a core application component used to store global application state. + * It may be accessed via {@link CApplication::getStatePersister)}. + * page state persistent method based on cache. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CStatePersister.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +class CStatePersister extends CApplicationComponent implements IStatePersister +{ + /** + * @var string the file path storing the state data. Make sure the directory containing + * the file exists and is writable by the Web server process. If using relative path, also + * make sure the path is correct. + */ + public $stateFile; + + /** + * Initializes the component. + * This method overrides the parent implementation by making sure {@link stateFile} + * contains valid value. + */ + public function init() + { + parent::init(); + if($this->stateFile===null) + $this->stateFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'state.bin'; + $dir=dirname($this->stateFile); + if(!is_dir($dir) || !is_writable($dir)) + throw new CException(Yii::t('yii','Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.', + array('{file}'=>$this->stateFile))); + } + + /** + * Loads state data from persistent storage. + * @return mixed state data. Null if no state data available. + */ + public function load() + { + $stateFile=$this->stateFile; + if(($cache=Yii::app()->getCache())!==null) + { + $cacheKey='Yii.CStatePersister.'.$stateFile; + if(($value=$cache->get($cacheKey))!==false) + return unserialize($value); + else if(($content=@file_get_contents($stateFile))!==false) + { + $cache->set($cacheKey,$content,0,new CFileCacheDependency($stateFile)); + return unserialize($content); + } + else + return null; + } + else if(($content=@file_get_contents($stateFile))!==false) + return unserialize($content); + else + return null; + } + + /** + * Saves application state in persistent storage. + * @param mixed state data (must be serializable). + */ + public function save($state) + { + file_put_contents($this->stateFile,serialize($state),LOCK_EX); + } +} diff --git a/web/framework-1.0.8/base/interfaces.php b/web/framework-1.0.8/base/interfaces.php new file mode 100644 index 0000000..4dffc3c --- /dev/null +++ b/web/framework-1.0.8/base/interfaces.php @@ -0,0 +1,534 @@ +<?php +/** + * This file contains core interfaces for Yii framework. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * IApplicationComponent is the interface that all application components must implement. + * + * After the application completes configuration, it will invoke the {@link init()} + * method of every loaded application component. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IApplicationComponent +{ + /** + * Initializes the application component. + * This method is invoked after the application completes configuration. + */ + public function init(); + /** + * @return boolean whether the {@link init()} method has been invoked. + */ + public function getIsInitialized(); +} + +/** + * ICache is the interface that must be implemented by cache components. + * + * This interface must be implemented by classes supporting caching feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +interface ICache +{ + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($id); + /** + * Retrieves multiple values from cache with the specified keys. + * Some caches (such as memcache, apc) allow retrieving multiple cached values at one time, + * which may improve the performance since it reduces the communication cost. + * In case a cache doesn't support this feature natively, it will be simulated by this method. + * @param array list of keys identifying the cached values + * @return array list of cached values corresponding to the specified keys. The array + * is returned in terms of (key,value) pairs. + * If a value is not cached or expired, the corresponding array value will be false. + * @since 1.0.8 + */ + public function mget($ids); + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labelled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null); + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labelled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null); + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean whether the deletion is successful + */ + public function delete($id); + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush(); +} + +/** + * ICacheDependency is the interface that must be implemented by cache dependency classes. + * + * This interface must be implemented by classes meant to be used as + * cache dependencies. + * + * Objects implementing this interface must be able to be serialized and unserialized. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +interface ICacheDependency +{ + /** + * Evaluates the dependency by generating and saving the data related with dependency. + * This method is invoked by cache before writing data into it. + */ + public function evaluateDependency(); + /** + * @return boolean whether the dependency has changed. + */ + public function getHasChanged(); +} + + +/** + * IStatePersister is the interface that must be implemented by state persister calsses. + * + * This interface must be implemented by all state persister classes (such as + * {@link CStatePersister}. + * + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IStatePersister +{ + /** + * Loads state data from a persistent storage. + * @return mixed the state + */ + public function load(); + /** + * Saves state data into a persistent storage. + * @param mixed the state to be saved + */ + public function save($state); +} + + +/** + * IFilter is the interface that must be implemented by action filters. + * + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IFilter +{ + /** + * Performs the filtering. + * This method should be implemented to perform actual filtering. + * If the filter wants to continue the action execution, it should call + * <code>$filterChain->run()</code>. + * @param CFilterChain the filter chain that the filter is on. + */ + public function filter($filterChain); +} + + +/** + * IAction is the interface that must be implemented by controller actions. + * + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IAction +{ + /** + * Runs the action. + * This method is invoked by the controller owning this action. + */ + public function run(); + /** + * @return string id of the action + */ + public function getId(); + /** + * @return CController the controller instance + */ + public function getController(); +} + + +/** + * IWebServiceProvider interface may be implemented by Web service provider classes. + * + * If this interface is implemented, the provider instance will be able + * to intercept the remote method invocation (e.g. for logging or authentication purpose). + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IWebServiceProvider +{ + /** + * This method is invoked before the requested remote method is invoked. + * @param CWebService the currently requested Web service. + * @return boolean whether the remote method should be executed. + */ + public function beforeWebMethod($service); + /** + * This method is invoked after the requested remote method is invoked. + * @param CWebService the currently requested Web service. + */ + public function afterWebMethod($service); +} + + +/** + * IViewRenderer interface is implemented by a view renderer class. + * + * A view renderer is {@link CWebApplication::viewRenderer viewRenderer} + * application component whose wants to replace the default view rendering logic + * implemented in {@link CBaseController}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IViewRenderer +{ + /** + * Renders a view file. + * @param CBaseController the controller or widget who is rendering the view file. + * @param string the view file path + * @param mixed the data to be passed to the view + * @param boolean whether the rendering result should be returned + * @return mixed the rendering result, or null if the rendering result is not needed. + */ + public function renderFile($context,$file,$data,$return); +} + + +/** + * IUserIdentity interface is implemented by a user identity class. + * + * An identity represents a way to authenticate a user and retrieve + * information needed to uniquely identity the user. It is normally + * used with the {@link CWebApplication::user user application component}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IUserIdentity +{ + /** + * Authenticates the user. + * The information needed to authenticate the user + * are usually provided in the constructor. + * @return boolean whether authentication succeeds. + */ + public function authenticate(); + /** + * Returns a value indicating whether the identity is authenticated. + * @return boolean whether the identity is valid. + */ + public function getIsAuthenticated(); + /** + * Returns a value that uniquely represents the identity. + * @return mixed a value that uniquely represents the identity (e.g. primary key value). + */ + public function getId(); + /** + * Returns the display name for the identity (e.g. username). + * @return string the display name for the identity. + */ + public function getName(); + /** + * Returns the additional identity information that needs to be persistent during the user session. + * @return array additional identity information that needs to be persistent during the user session (excluding {@link id}). + */ + public function getPersistentStates(); +} + + +/** + * IWebUser interface is implemented by a {@link CWebApplication::user user application component}. + * + * A user application component represents the identity information + * for the current user. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IWebUser +{ + /** + * Returns a value that uniquely represents the identity. + * @return mixed a value that uniquely represents the identity (e.g. primary key value). + */ + public function getId(); + /** + * Returns the display name for the identity (e.g. username). + * @return string the display name for the identity. + */ + public function getName(); + /** + * Returns a value indicating whether the user is a guest (not authenticated). + * @return boolean whether the user is a guest (not authenticated) + */ + public function getIsGuest(); + /** + * Performs access check for this user. + * @param string the name of the operation that need access check. + * @param array name-value pairs that would be passed to business rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by this user. + */ + public function checkAccess($operation,$params=array()); +} + + +/** + * IAuthManager interface is implemented by an auth manager application component. + * + * An auth manager is mainly responsible for providing role-based access control (RBAC) service. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0 + */ +interface IAuthManager +{ + /** + * Performs access check for the specified user. + * @param string the name of the operation that need access check + * @param mixed the user ID. This should can be either an integer and a string representing + * the unique identifier of a user. See {@link IWebUser::getId}. + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user. + */ + public function checkAccess($itemName,$userId,$params=array()); + + /** + * Creates an authorization item. + * An authorization item represents an action permission (e.g. creating a post). + * It has three types: operation, task and role. + * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * by lower level items. + * @param string the item name. This must be a unique identifier. + * @param integer the item type (0: operation, 1: task, 2: role). + * @param string description of the item + * @param string business rule associated with the item. This is a piece of + * PHP code that will be executed when {@link checkAccess} is called for the item. + * @param mixed additional data associated with the item. + * @return CAuthItem the authorization item + * @throws CException if an item with the same name already exists + */ + public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null); + /** + * Removes the specified authorization item. + * @param string the name of the item to be removed + * @return boolean whether the item exists in the storage and has been removed + */ + public function removeAuthItem($name); + /** + * Returns the authorization items of the specific type and user. + * @param integer the item type (0: operation, 1: task, 2: role). Defaults to null, + * meaning returning all items regardless of their type. + * @param mixed the user ID. Defaults to null, meaning returning all items even if + * they are not assigned to a user. + * @return array the authorization items of the specific type. + */ + public function getAuthItems($type=null,$userId=null); + /** + * Returns the authorization item with the specified name. + * @param string the name of the item + * @return CAuthItem the authorization item. Null if the item cannot be found. + */ + public function getAuthItem($name); + /** + * Saves an authorization item to persistent storage. + * @param CAuthItem the item to be saved. + * @param string the old item name. If null, it means the item name is not changed. + */ + public function saveAuthItem($item,$oldName=null); + + /** + * Adds an item as a child of another item. + * @param string the parent item name + * @param string the child item name + * @throws CException if either parent or child doesn't exist or if a loop has been detected. + */ + public function addItemChild($itemName,$childName); + /** + * Removes a child from its parent. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the removal is successful + */ + public function removeItemChild($itemName,$childName); + /** + * Returns a value indicating whether a child exists within a parent. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the child exists + */ + public function hasItemChild($itemName,$childName); + /** + * Returns the children of the specified item. + * @param mixed the parent item name. This can be either a string or an array. + * The latter represents a list of item names (available since version 1.0.5). + * @return array all child items of the parent + */ + public function getItemChildren($itemName); + + /** + * Assigns an authorization item to a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @param string the business rule to be executed when {@link checkAccess} is called + * for this particular authorization item. + * @param mixed additional data associated with this assignment + * @return CAuthAssignment the authorization assignment information. + * @throws CException if the item does not exist or if the item has already been assigned to the user + */ + public function assign($itemName,$userId,$bizRule=null,$data=null); + /** + * Revokes an authorization assignment from a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether removal is successful + */ + public function revoke($itemName,$userId); + /** + * Returns a value indicating whether the item has been assigned to the user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether the item has been assigned to the user. + */ + public function isAssigned($itemName,$userId); + /** + * Returns the item assignment information. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return CAuthAssignment the item assignment information. Null is returned if + * the item is not assigned to the user. + */ + public function getAuthAssignment($itemName,$userId); + /** + * Returns the item assignments for the specified user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return array the item assignment information for the user. An empty array will be + * returned if there is no item assigned to the user. + */ + public function getAuthAssignments($userId); + /** + * Saves the changes to an authorization assignment. + * @param CAuthAssignment the assignment that has been changed. + */ + public function saveAuthAssignment($assignment); + + /** + * Removes all authorization data. + */ + public function clearAll(); + /** + * Removes all authorization assignments. + */ + public function clearAuthAssignments(); + + /** + * Saves authorization data into persistent storage. + * If any change is made to the authorization data, please make + * sure you call this method to save the changed data into persistent storage. + */ + public function save(); + + /** + * Executes a business rule. + * A business rule is a piece of PHP code that will be executed when {@link checkAccess} is called. + * @param string the business rule to be executed. + * @param array additional parameters to be passed to the business rule when being executed. + * @param mixed additional data that is associated with the corresponding authorization item or assignment + * @return whether the execution returns a true value. + * If the business rule is empty, it will also return true. + */ + public function executeBizRule($bizRule,$params,$data); +} + +/** + * IBehavior interfaces is implemented by all behavior classes. + * + * A behavior is a way to enhance a component with additional methods that + * are defined in the behavior class and not available in the component class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: interfaces.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.base + * @since 1.0.2 + */ +interface IBehavior +{ + /** + * Attaches the behavior object to the component. + * @param CComponent the component that this behavior is to be attached to. + */ + public function attach($component); + /** + * Detaches the behavior object from the component. + * @param CComponent the component that this behavior is to be detached from. + */ + public function detach($component); + /** + * @return boolean whether this behavior is enabled + */ + public function getEnabled(); + /** + * @param boolean whether this behavior is enabled + */ + public function setEnabled($value); +} diff --git a/web/framework-1.0.8/caching/CApcCache.php b/web/framework-1.0.8/caching/CApcCache.php new file mode 100644 index 0000000..12e6875 --- /dev/null +++ b/web/framework-1.0.8/caching/CApcCache.php @@ -0,0 +1,108 @@ +<?php +/** + * CApcCache class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CApcCache provides APC caching in terms of an application component. + * + * The caching is based on {@link http://www.php.net/apc APC}. + * To use this application component, the APC PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CApcCache. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CApcCache.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +class CApcCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if APC cache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if(!extension_loaded('apc')) + throw new CException(Yii::t('yii','CApcCache requires PHP apc extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return apc_fetch($key); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + * @since 1.0.8 + */ + protected function getValues($keys) + { + return array_combine($keys,apc_fetch($keys)); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return apc_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return apc_add($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return apc_delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return apc_clear_cache('user'); + } +} diff --git a/web/framework-1.0.8/caching/CCache.php b/web/framework-1.0.8/caching/CCache.php new file mode 100644 index 0000000..17dd2c9 --- /dev/null +++ b/web/framework-1.0.8/caching/CCache.php @@ -0,0 +1,312 @@ +<?php +/** + * CCache class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCache is the base class for cache classes with different cache storage implementation. + * + * A data item can be stored in cache by calling {@link set} and be retrieved back + * later by {@link get}. In both operations, a key identifying the data item is required. + * An expiration time and/or a dependency can also be specified when calling {@link set}. + * If the data item expires or the dependency changes, calling {@link get} will not + * return back the data item. + * + * Note, by definition, cache does not ensure the existence of a value + * even if it does not expire. Cache is not meant to be a persistent storage. + * + * CCache implements the interface {@link ICache} with the following methods: + * <ul> + * <li>{@link get} : retrieve the value with a key (if any) from cache</li> + * <li>{@link set} : store the value with a key into cache</li> + * <li>{@link add} : store the value only if cache does not have this key</li> + * <li>{@link delete} : delete the value with the specified key from cache</li> + * <li>{@link flush} : delete all values from cache</li> + * </ul> + * + * Child classes must implement the following methods: + * <ul> + * <li>{@link getValue}</li> + * <li>{@link setValue}</li> + * <li>{@link addValue}</li> + * <li>{@link deleteValue}</li> + * <li>{@link flush} (optional)</li> + * </ul> + * + * CCache also implements ArrayAccess so that it can be used like an array. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CCache.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +abstract class CCache extends CApplicationComponent implements ICache, ArrayAccess +{ + /** + * @var string a string prefixed to every cache key so that it is unique. Defaults to {@link CApplication::getId() application ID}. + */ + public $keyPrefix; + + /** + * Initializes the application component. + * This method overrides the parent implementation by setting default cache key prefix. + */ + public function init() + { + parent::init(); + if($this->keyPrefix===null) + $this->keyPrefix=Yii::app()->getId(); + } + + /** + * @param string a key identifying a value to be cached + * @return sring a key generated from the provided key which ensures the uniqueness across applications + */ + protected function generateUniqueKey($key) + { + return md5($this->keyPrefix.$key); + } + + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed. + */ + public function get($id) + { + if(($value=$this->getValue($this->generateUniqueKey($id)))!==false) + { + $data=unserialize($value); + if(!is_array($data)) + return false; + if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) + { + Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this)); + return $data[0]; + } + } + return false; + } + + /** + * Retrieves multiple values from cache with the specified keys. + * Some caches (such as memcache, apc) allow retrieving multiple cached values at one time, + * which may improve the performance since it reduces the communication cost. + * In case a cache doesn't support this feature natively, it will be simulated by this method. + * @param array list of keys identifying the cached values + * @return array list of cached values corresponding to the specified keys. The array + * is returned in terms of (key,value) pairs. + * If a value is not cached or expired, the corresponding array value will be false. + * @since 1.0.8 + */ + public function mget($ids) + { + $values=$this->getValues($ids); + $results=array(); + foreach($values as $id=>$value) + { + $data=unserialize($value); + if(is_array($data) && (!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged())) + { + Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this)); + $results[$id]=$data[0]; + } + else + $results[$id]=false; + } + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + Yii::trace('Saving "'.$id.'" to cache','system.caching.'.get_class($this)); + if($dependency!==null) + $dependency->evaluateDependency(); + $data=array($value,$dependency); + return $this->setValue($this->generateUniqueKey($id),serialize($data),$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + Yii::trace('Adding "'.$id.'" to cache','system.caching.'.get_class($this)); + if($dependency!==null) + $dependency->evaluateDependency(); + $data=array($value,$dependency); + return $this->addValue($this->generateUniqueKey($id),serialize($data),$expire); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + Yii::trace('Deleting "'.$id.'" to cache','system.caching.'.get_class($this)); + return $this->deleteValue($this->generateUniqueKey($id)); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * Child classes may implement this method to realize the flush operation. + * @throws CException if this method is not overridden by child classes + */ + public function flush() + { + Yii::trace('Flushign cache','system.caching.'.get_class($this)); + throw new CException(Yii::t('yii','{className} does not support flush() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Retrieves a value from cache with a specified key. + * This method should be implemented by child classes to retrieve the data + * from specific cache storage. The uniqueness and dependency are handled + * in {@link get()} already. So only the implementation of data retrieval + * is needed. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + throw new CException(Yii::t('yii','{className} does not support get() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * The default implementation simply calls {@link getValue} multiple + * times to retrieve the cached values one by one. + * If the underlying cache storage supports multiget, this method should + * be overridden to exploit that feature. + * @param array a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + * @since 1.0.8 + */ + protected function getValues($keys) + { + $results=array(); + foreach($keys as $key) + $results[$key]=$this->getValue($key); + return $results; + } + + /** + * Stores a value identified by a key in cache. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link set()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + throw new CException(Yii::t('yii','{className} does not support set() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link add()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + throw new CException(Yii::t('yii','{className} does not support add() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Deletes a value with the specified key from cache + * This method should be implemented by child classes to delete the data from actual cache storage. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + throw new CException(Yii::t('yii','{className} does not support delete() functionality.', + array('{className}'=>get_class($this)))); + } + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return $this->get($id)!==false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return $this->get($id); + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function offsetSet($id, $value) + { + $this->set($id, $value); + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + $this->delete($id); + } +} diff --git a/web/framework-1.0.8/caching/CDbCache.php b/web/framework-1.0.8/caching/CDbCache.php new file mode 100644 index 0000000..cc6a513 --- /dev/null +++ b/web/framework-1.0.8/caching/CDbCache.php @@ -0,0 +1,250 @@ +<?php +/** + * CDbCache class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCache implements a cache application component by storing cached data in a database. + * + * CDbCache stores cache data in a DB table named {@link cacheTableName}. + * If the table does not exist, it will be automatically created. + * By setting {@link autoCreateCacheTable} to false, you can also manually create the DB table. + * + * CDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database. + * By default, it will use a SQLite3 database under the application runtime directory. + * You can also specify {@link connectionID} so that it makes use of + * a DB application component to access database. + * + * See {@link CCache} manual for common cache operations that are supported by CDbCache. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbCache.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +class CDbCache extends CCache +{ + /** + * @var string the ID of the {@link CDbConnection} application component. If not set, + * a SQLite3 database will be automatically created and used. The SQLite database file + * is <code>protected/runtime/cache-YiiVersion.db</code>. + */ + public $connectionID; + /** + * @var string name of the DB table to store cache content. Defaults to 'YiiCache'. + * Note, if {@link autoCreateCacheTable} is false and you want to create the DB table + * manually by yourself, you need to make sure the DB table is of the following structure: + * <pre> + * (id CHAR(128) PRIMARY KEY, expire INTEGER, value BLOB) + * </pre> + * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * @see autoCreateCacheTable + */ + public $cacheTableName='YiiCache'; + /** + * @var boolean whether the cache DB table should be created automatically if it does not exist. Defaults to true. + * @see cacheTableName + */ + public $autoCreateCacheTable=true; + /** + * @var CDbConnection the DB connection instance + */ + private $_db; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->_db!==null) + $this->_db->setActive(false); + } + + /** + * Initializes this application component. + * + * This method is required by the {@link IApplicationComponent} interface. + * It ensures the existence of the cache DB table. + * It also removes expired data items from the cache. + */ + public function init() + { + parent::init(); + + $db=$this->getDbConnection(); + $db->setActive(true); + + $sql="DELETE FROM {$this->cacheTableName} WHERE expire>0 AND expire<".time(); + try + { + $db->createCommand($sql)->execute(); + } + catch(Exception $e) + { + // The cache table does not exist + if($this->autoCreateCacheTable) + $this->createCacheTable($db,$this->cacheTableName); + else + throw new CException(Yii::t('yii','Cache table "{tableName}" does not exist.', + array('{tableName}'=>$this->cacheTableName))); + } + } + + /** + * Creates the cache DB table. + * @param CDbConnection the database connection + * @param string the name of the table to be created + */ + protected function createCacheTable($db,$tableName) + { + $driver=$db->getDriverName(); + if($driver==='mysql') + $blob='LONGBLOB'; + else if($driver==='pgsql') + $blob='BYTEA'; + else + $blob='BLOB'; + $sql=<<<EOD +CREATE TABLE $tableName +( + id CHAR(128) PRIMARY KEY, + expire INTEGER, + value $blob +) +EOD; + $db->createCommand($sql)->execute(); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($id=$this->connectionID)!==null) + { + if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$id))); + } + else + { + $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache-'.Yii::getVersion().'.db'; + return $this->_db=new CDbConnection('sqlite:'.$dbFile); + } + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $time=time(); + $sql="SELECT value FROM {$this->cacheTableName} WHERE id='$key' AND (expire=0 OR expire>$time)"; + return $this->getDbConnection()->createCommand($sql)->queryScalar(); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + * @since 1.0.8 + */ + protected function getValues($keys) + { + if(empty($keys)) + return array(); + + $ids=implode("','",$keys); + $time=time(); + $sql="SELECT id, value FROM {$this->cacheTableName} WHERE id IN ('$ids') AND (expire=0 OR expire>$time)"; + $rows=$this->getDbConnection()->createCommand($sql)->queryRows(); + $results=array(); + foreach($keys as $key) + $results[$key]=false; + foreach($rows as $row) + $results[$row['id']]=$results[$row['value']]; + return $results; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $this->deleteValue($key); + return $this->addValue($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if($expire>0) + $expire+=time(); + else + $expire=0; + $sql="INSERT INTO {$this->cacheTableName} (id,expire,value) VALUES ('$key',$expire,:value)"; + try + { + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':value',$value,PDO::PARAM_LOB); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $sql="DELETE FROM {$this->cacheTableName} WHERE id='$key'"; + $this->getDbConnection()->createCommand($sql)->execute(); + return true; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + $this->getDbConnection()->createCommand("DELETE FROM {$this->cacheTableName}")->execute(); + return true; + } +} diff --git a/web/framework-1.0.8/caching/CDummyCache.php b/web/framework-1.0.8/caching/CDummyCache.php new file mode 100644 index 0000000..a908fa6 --- /dev/null +++ b/web/framework-1.0.8/caching/CDummyCache.php @@ -0,0 +1,145 @@ +<?php +/** + * CDummyCache class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDummyCache is a placeholder cache component. + * + * CDummyCache does not do/cache anything. It is used as the default 'cache' application component. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDummyCache.php 1320 2009-08-09 04:55:50Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +class CDummyCache extends CApplicationComponent implements ICache, ArrayAccess +{ + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed. + */ + public function get($id) + { + return false; + } + + /** + * Retrieves multiple values from cache with the specified keys. + * Some caches (such as memcache, apc) allow retrieving multiple cached values at one time, + * which may improve the performance since it reduces the communication cost. + * In case a cache doesn't support this feature natively, it will be simulated by this method. + * @param array list of keys identifying the cached values + * @return array list of cached values corresponding to the specified keys. The array + * is returned in terms of (key,value) pairs. + * If a value is not cached or expired, the corresponding array value will be false. + * @since 1.0.8 + */ + public function mget($ids) + { + $results=array(); + foreach($ids as $id) + $results[$id]=false; + return $results; + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + return true; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + return true; + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + return true; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * Child classes may implement this method to realize the flush operation. + * @throws CException if this method is not overridden by child classes + */ + public function flush() + { + } + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return false; + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function offsetSet($id, $value) + { + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + } +} diff --git a/web/framework-1.0.8/caching/CEAcceleratorCache.php b/web/framework-1.0.8/caching/CEAcceleratorCache.php new file mode 100644 index 0000000..639f1d8 --- /dev/null +++ b/web/framework-1.0.8/caching/CEAcceleratorCache.php @@ -0,0 +1,104 @@ +<?php +/** + * CEAcceleratorCache class file + * + * @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CEAcceleratorCache implements a cache application module based on {@link http://eaccelerator.net/ eaccelerator}. + * + * To use this application component, the eAccelerator PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CEAccelerator. + * + * @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com> + * @version $Id: CEAcceleratorCache.php 783 2009-03-04 12:39:00Z qiang.xue $ + * @package system.caching + * @since 1.0.4 + */ +class CEAcceleratorCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of eAccelerator. + * @throws CException if eAccelerator extension is not loaded, is disabled or the cache functions are not compiled in. + */ + public function init() + { + parent::init(); + if(!function_exists('eaccelerator_get')) + throw new CException(Yii::t('yii','CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $result = eaccelerator_get($key); + return $result !== NULL ? $result : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return eaccelerator_put($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return (NULL === eaccelerator_get($key)) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return eaccelerator_rm($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + // first, remove expired content from cache + eaccelerator_gc(); + + // now, remove leftover cache-keys + $keys = eaccelerator_list_keys(); + foreach($keys as $key) + $this->deleteValue(substr($key['name'], 1)); + } +} + diff --git a/web/framework-1.0.8/caching/CFileCache.php b/web/framework-1.0.8/caching/CFileCache.php new file mode 100644 index 0000000..92ac423 --- /dev/null +++ b/web/framework-1.0.8/caching/CFileCache.php @@ -0,0 +1,217 @@ +<?php +/** + * CFileCache class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileCache provides a file-based caching mechanism. + * + * For each data value being cached, CFileCache will use store it in a separate file + * under {@link cachePath} which defaults to 'protected/runtime/cache'. + * CFileCache will perform garbage collection automatically to remove expired cache files. + * + * See {@link CCache} manual for common cache operations that are supported by CFileCache. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileCache.php 1097 2009-06-06 00:01:13Z qiang.xue $ + * @package system.caching + * @since 1.0.6 + */ +class CFileCache extends CCache +{ + /** + * @var string the directory to store cache files. Defaults to null, meaning + * using 'protected/runtime/cache' as the directory. + */ + public $cachePath; + /** + * @var string cache file suffix. Defaults to '.bin'. + */ + public $cacheFileSuffix='.bin'; + /** + * @var integer the level of sub-directories to store cache files. Defaults to 0, + * meaning no sub-directories. If the system has huge number of cache files (e.g. 10K+), + * you may want to set this value to be 1 or 2 so that the file system is not over burdened. + * The value of this property should not exceed 16 (less than 3 is recommended). + */ + public $directoryLevel=0; + + private $_gcProbability=100; + private $_gced=false; + + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if APC cache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if($this->cachePath===null) + $this->cachePath=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache'; + if(!is_dir($this->cachePath)) + mkdir($this->cachePath,0777,true); + } + + /** + * @return integer the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + */ + public function getGCProbability() + { + return $this->_gcProbability; + } + + /** + * @param integer the probability (parts per million) that garbage collection (GC) should be performed + * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. + * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all. + */ + public function setGCProbability($value) + { + $value=(int)$value; + if($value<0) + $value=0; + if($value>1000000) + $value=1000000; + $this->_gcProbability=$value; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->gc(false); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $cacheFile=$this->getCacheFile($key); + if(($time=@filemtime($cacheFile))>time()) + return file_get_contents($cacheFile); + else if($time>0) + @unlink($cacheFile); + return false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability) + { + $this->gc(); + $this->_gced=true; + } + + if($expire<=0) + $expire=31536000; // 1 year + $expire+=time(); + + $cacheFile=$this->getCacheFile($key); + if($this->directoryLevel>0) + @mkdir(dirname($cacheFile),0777,true); + if(@file_put_contents($cacheFile,$value,LOCK_EX)==strlen($value)) + { + @chmod($cacheFile,0777); + return @touch($cacheFile,$expire); + } + else + return false; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + $cacheFile=$this->getCacheFile($key); + if(@filemtime($cacheFile)>time()) + return false; + return $this->setValue($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $cacheFile=$this->getCacheFile($key); + return @unlink($cacheFile); + } + + /** + * Returns the cache file path given the cache key. + * @param string cache key + * @return string the cache file path + */ + protected function getCacheFile($key) + { + if($this->directoryLevel>0) + { + $base=$this->cachePath; + for($i=0;$i<$this->directoryLevel;++$i) + { + if(($prefix=substr($key,$i+$i,2))!==false) + $base.=DIRECTORY_SEPARATOR.$prefix; + } + return $base.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix; + } + else + return $this->cachePath.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix; + } + + /** + * Removes expired cache files. + * @param boolean whether to removed expired cache files only. If true, all cache files under {@link cachePath} will be removed. + * @param string the path to clean with. If null, it will be {@link cachePath}. + */ + protected function gc($expiredOnly=true,$path=null) + { + if($path===null) + $path=$this->cachePath; + if(($handle=opendir($path))===false) + return; + while($file=readdir($handle)) + { + if($file[0]==='.') + continue; + $fullPath=$path.DIRECTORY_SEPARATOR.$file; + if(is_dir($fullPath)) + $this->gc($expiredOnly,$fullPath); + else if($expiredOnly && @filemtime($fullPath)<time() || !$expiredOnly) + @unlink($fullPath); + } + closedir($handle); + } +} diff --git a/web/framework-1.0.8/caching/CMemCache.php b/web/framework-1.0.8/caching/CMemCache.php new file mode 100644 index 0000000..890d6b8 --- /dev/null +++ b/web/framework-1.0.8/caching/CMemCache.php @@ -0,0 +1,272 @@ +<?php +/** + * CMemCache class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMemCache implements a cache application component based on {@link http://www.danga.com/memcached/ memcached}. + * + * CMemCache can be configured with a list of memcache servers by settings + * its {@link setServers servers} property. By default, CMemCache assumes + * there is a memcache server running on localhost at port 11211. + * + * See {@link CCache} manual for common cache operations that are supported by CMemCache. + * + * Note, there is no security measure to protected data in memcache. + * All data in memcache can be accessed by any process running in the system. + * + * To use CMemCache as the cache application component, configure the application as follows, + * <pre> + * array( + * 'components'=>array( + * 'cache'=>array( + * 'class'=>'CMemCache', + * 'servers'=>array( + * array( + * 'host'=>'server1', + * 'port'=>11211, + * 'weight'=>60, + * ), + * array( + * 'host'=>'server2', + * 'port'=>11211, + * 'weight'=>40, + * ), + * ), + * ), + * ), + * ) + * </pre> + * In the above, two memcache servers are used: server1 and server2. + * You can configure more properties of every server, including: + * host, port, persistent, weight, timeout, retryInterval, status. + * See {@link http://www.php.net/manual/en/function.memcache-addserver.php} + * for more details. + * + * Since version 1.0.6, CMemCache can also be used with {@link http://pecl.php.net/package/memcached memcached}. + * To do so, set {@link useMemcached} to be true. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMemCache.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +class CMemCache extends CCache +{ + /** + * @var boolean whether to use {@link http://pecl.php.net/package/memcached memcached} + * as the underlying caching extension. Defaults to false, meaning using + * {@link http://pecl.php.net/package/memcache memcache}. + * @since 1.0.6 + */ + public $useMemcached=false; + /** + * @var Memcache the Memcache instance + */ + private $_cache=null; + /** + * @var array list of memcache server configurations + */ + private $_servers=array(); + + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It creates the memcache instance and adds memcache servers. + * @throws CException if memcache extension is not loaded + */ + public function init() + { + parent::init(); + $servers=$this->getServers(); + $cache=$this->getMemCache(); + if(count($servers)) + { + foreach($servers as $server) + { + if($this->useMemcached) + $cache->addServer($server->host,$server->port,$server->weight); + else + $cache->addServer($server->host,$server->port,$server->persistent,$server->weight,$server->timeout,$server->status); + } + } + else + $cache->addServer('localhost',11211); + } + + /** + * @return mixed the memcache instance (or memcached if {@link useMemcached} is true) used by this component. + */ + public function getMemCache() + { + if($this->_cache!==null) + return $this->_cache; + else + return $this->_cache=$this->useMemcached ? new Memcached : new Memcache; + } + + /** + * @return array list of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}. + */ + public function getServers() + { + return $this->_servers; + } + + /** + * @param array list of memcache server configurations. Each element must be an array + * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. + * @see http://www.php.net/manual/en/function.Memcache-addServer.php + */ + public function setServers($config) + { + foreach($config as $c) + $this->_servers[]=new CMemCacheServerConfiguration($c); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return $this->_cache->get($key); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + * @since 1.0.8 + */ + protected function getValues($keys) + { + return array_combine($keys,$this->_cache->get($keys)); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + if($expire>0) + $expire+=time(); + else + $expire=0; + + return $this->_cache->set($key,$value,0,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if($expire>0) + $expire+=time(); + else + $expire=0; + + return $this->_cache->add($key,$value,0,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return $this->_cache->delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_cache->flush(); + } +} + +/** + * CMemCacheServerConfiguration represents the configuration data for a single memcache server. + * + * See {@link http://www.php.net/manual/en/function.Memcache-addServer.php} + * for detailed explanation of each configuration property. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMemCache.php 1290 2009-08-06 16:13:11Z qiang.xue $ + * @package system.caching + * @since 1.0 + */ +class CMemCacheServerConfiguration extends CComponent +{ + /** + * @var string memcache server hostname or IP address + */ + public $host; + /** + * @var integer memcache server port + */ + public $port=11211; + /** + * @var boolean whether to use a persistent connection + */ + public $persistent=true; + /** + * @var integer probability of using this server among all servers. + */ + public $weight=1; + /** + * @var integer value in seconds which will be used for connecting to the server + */ + public $timeout=15; + /** + * @var integer how often a failed server will be retried (in seconds) + */ + public $retryInterval=15; + /** + * @var boolean if the server should be flagged as online upon a failure + */ + public $status=true; + + /** + * Constructor. + * @param array list of memcache server configurations. + * @throws CException if the configuration is not an array + */ + public function __construct($config) + { + if(is_array($config)) + { + foreach($config as $key=>$value) + $this->$key=$value; + if($this->host===null) + throw new CException(Yii::t('yii','CMemCache server configuration must have "host" value.')); + } + else + throw new CException(Yii::t('yii','CMemCache server configuration must be an array.')); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/caching/CXCache.php b/web/framework-1.0.8/caching/CXCache.php new file mode 100644 index 0000000..f5edb15 --- /dev/null +++ b/web/framework-1.0.8/caching/CXCache.php @@ -0,0 +1,97 @@ +<?php +/** + * CXCache class file + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}. + * + * To use this application component, the XCache PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CXCache. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id: CXCache.php 1084 2009-06-02 17:35:38Z qiang.xue $ + * @package system.caching + * @since 1.0.1 + */ +class CXCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of memcache. + * @throws CException if memcache extension is not loaded or is disabled. + */ + public function init() + { + parent::init(); + if(!function_exists('xcache_isset')) + throw new CException(Yii::t('yii','CXCache requires PHP XCache extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return xcache_isset($key) ? xcache_get($key) : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return xcache_set($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return xcache_unset($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return xcache_clear_cache(); + } +} + diff --git a/web/framework-1.0.8/caching/CZendDataCache.php b/web/framework-1.0.8/caching/CZendDataCache.php new file mode 100644 index 0000000..14c8dd5 --- /dev/null +++ b/web/framework-1.0.8/caching/CZendDataCache.php @@ -0,0 +1,98 @@ +<?php +/** + * CZendDataCache class file + * + * @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CZendDataCache implements a cache application module based on the Zend Data Cache + * delivered with {@link http://www.zend.com/en/products/server/ ZendServer}. + * + * To use this application component, the Zend Data Cache PHP extension must be loaded. + * + * See {@link CCache} manual for common cache operations that are supported by CZendDataCache. + * + * @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com> + * @version $Id: CZendDataCache.php 852 2009-03-20 14:14:44Z qiang.xue $ + * @package system.caching + * @since 1.0.4 + */ +class CZendDataCache extends CCache +{ + /** + * Initializes this application component. + * This method is required by the {@link IApplicationComponent} interface. + * It checks the availability of Zend Data Cache. + * @throws CException if Zend Data Cache extension is not loaded. + */ + public function init() + { + parent::init(); + if(!function_exists('zend_shm_cache_store')) + throw new CException(Yii::t('yii','CZendDataCache requires PHP Zend Data Cache extension to be loaded.')); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $result = zend_shm_cache_fetch($key); + return $result !== NULL ? $result : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return zend_shm_cache_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return (NULL === zend_shm_cache_fetch($key)) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return zend_shm_cache_delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + zend_shm_cache_clear(); + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CCacheDependency.php new file mode 100644 index 0000000..ec705b8 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CCacheDependency.php @@ -0,0 +1,62 @@ +<?php +/** + * CCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCacheDependency is the base class for cache dependency classes. + * + * CCacheDependency implements the {@link ICacheDependency} interface. + * Child classes should override its {@link generateDependentData} for + * actual dependency checking. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CCacheDependency.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CCacheDependency extends CComponent implements ICacheDependency +{ + private $_data; + + /** + * Evaluates the dependency by generating and saving the data related with dependency. + * This method is invoked by cache before writing data into it. + */ + public function evaluateDependency() + { + $this->_data=$this->generateDependentData(); + } + + /** + * @return boolean whether the dependency has changed. + */ + public function getHasChanged() + { + return $this->generateDependentData()!=$this->_data; + } + + /** + * @return mixed the data used to determine if dependency has been changed. + * This data is available after {@link evaluateDependency} is called. + */ + public function getDependentData() + { + return $this->_data; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * Derived classes should override this method to generate actual dependent data. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + return null; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/caching/dependencies/CChainedCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CChainedCacheDependency.php new file mode 100644 index 0000000..7475c3f --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CChainedCacheDependency.php @@ -0,0 +1,68 @@ +<?php +/** + * CChainedCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CChainedCacheDependency represents a list of cache dependencies. + * + * If any of the dependencies reports a dependency change, CChainedCacheDependency + * will return true for the checking. + * + * To add dependencies to CChainedCacheDependency, use {@link getDependencies Dependencies} + * which gives a {@link CTypedList} instance and can be used like an array + * (see {@link CList} for more details}). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CChainedCacheDependency.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CChainedCacheDependency extends CComponent implements ICacheDependency +{ + private $_dependencies=null; + + /** + * @return CTypedList list of dependency objects + */ + public function getDependencies() + { + if($this->_dependencies===null) + $this->_dependencies=new CTypedList('ICacheDependency'); + return $this->_dependencies; + } + + /** + * Evaluates the dependency by generating and saving the data related with dependency. + */ + public function evaluateDependency() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + $dependency->evaluateDependency(); + } + } + + /** + * Performs the actual dependency checking. + * This method returns true if any of the dependency objects + * reports a dependency change. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + if($dependency->getHasChanged()) + return true; + } + return false; + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CDbCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CDbCacheDependency.php new file mode 100644 index 0000000..a7abd21 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CDbCacheDependency.php @@ -0,0 +1,87 @@ +<?php +/** + * CDbCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCacheDependency represents a dependency based on the query result of a SQL statement. + * + * If the query result (a scalar) changes, the dependency is considered as changed. + * To specify the SQL statement, set {@link sql} property. + * The {@link connectionID} property specifies the ID of a {@link CDbConnection} application + * component. It is this DB connection that is used to perform the query. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbCacheDependency.php 1276 2009-07-23 20:19:07Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CDbCacheDependency extends CCacheDependency +{ + /** + * @var string the ID of a {@link CDbConnection} application component. Defaults to 'db'. + */ + public $connectionID='db'; + /** + * @var string the SQL statement whose result is used to determine if the dependency has been changed. + * Note, the SQL statement should return back a single value. + */ + public $sql; + + private $_db; + + /** + * Constructor. + * @param string the SQL statement whose result is used to determine if the dependency has been changed. + */ + public function __construct($sql=null) + { + $this->sql=$sql; + } + + /** + * PHP sleep magic method. + * This method ensures that the database instance is set null because it contains resource handles. + */ + public function __sleep() + { + $this->_db=null; + return array_keys((array)$this); + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the value of the global state. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->sql!==null) + return $this->getDbConnection()->createCommand($this->sql)->queryScalar(); + else + throw new CException(Yii::t('yii','CDbCacheDependency.sql cannot be empty.')); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else + { + if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$this->connectionID))); + } + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CDirectoryCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CDirectoryCacheDependency.php new file mode 100644 index 0000000..4fa55a9 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CDirectoryCacheDependency.php @@ -0,0 +1,134 @@ +<?php +/** + * CDirectoryCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDirectoryCacheDependency represents a dependency based on change of a directory. + * + * CDirectoryCacheDependency performs dependency checking based on the + * modification time of the files contained in the specified directory. + * The directory being checked is specified via {@link directory}. + * + * By default, all files under the specified directory and subdirectories + * will be checked. If the last modification time of any of them is changed + * or if different number of files are contained in a directory, the dependency + * is reported as changed. By specifying {@link recursiveLevel}, + * one can limit the checking to a certain depth of the directory. + * + * Note, dependency checking for a directory is expensive because it involves + * accessing modification time of multiple files under the directory. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDirectoryCacheDependency.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CDirectoryCacheDependency extends CCacheDependency +{ + /** + * @var string the directory whose change is used to determine if the dependency has been changed. + * If any of the files under the directory is changed, the dependency is considered as changed. + */ + public $directory; + /** + * @var integer the depth of the subdirectories to be recursively checked. + * If the value is less than 0, it means unlimited depth. + * If the value is 0, it means checking the files directly under the specified directory. + */ + public $recursiveLevel=-1; + /** + * @var string the regular expression matching valid file/directory names. + * Only the matching files or directories will be checked for changes. + * Defaults to null, meaning all files/directories will qualify. + */ + public $namePattern; + + /** + * Constructor. + * @param string the directory to be checked + */ + public function __construct($directory=null) + { + $this->directory=$directory; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the modification timestamps for files under the directory. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->directory!==null) + return $this->generateTimestamps($this->directory); + else + throw new CException(Yii::t('yii','CDirectoryCacheDependency.directory cannot be empty.')); + } + + /** + * Determines the last modification time for files under the directory. + * This method may go recursively into subdirectories if {@link recursiveLevel} is not 0. + * @param string the directory name + * @param int level of the recursion + * @return array list of file modification time indexed by the file path + */ + protected function generateTimestamps($directory,$level=0) + { + if(($dir=@opendir($directory))===false) + throw new CException(Yii::t('yii','"{path}" is not a valid directory.', + array('{path}'=>$directory))); + $timestamps=array(); + while(($file=readdir($dir))!==false) + { + $path=$directory.DIRECTORY_SEPARATOR.$file; + if($file==='.' || $file==='..') + continue; + if($this->namePattern!==null && !preg_match($this->namePattern,$file)) + continue; + if(is_file($path)) + { + if($this->validateFile($path)) + $timestamps[$path]=filemtime($path); + } + else + { + if(($this->recursiveLevel<0 || $level<$this->recursiveLevel) && $this->validateDirectory($path)) + $timestamps=array_merge($this->generateTimestamps($path,$level+1)); + } + } + closedir($dir); + return $timestamps; + } + + /** + * Checks to see if the file should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the file should be checked. + * You may override this method to check only certain files. + * @param string the name of the file that may be checked for dependency. + * @return boolean whether this file should be checked. + */ + protected function validateFile($fileName) + { + return true; + } + + /** + * Checks to see if the specified subdirectory should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the subdirectory should be checked. + * You may override this method to check only certain subdirectories. + * @param string the name of the subdirectory that may be checked for dependency. + * @return boolean whether this subdirectory should be checked. + */ + protected function validateDirectory($directory) + { + return true; + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CExpressionDependency.php b/web/framework-1.0.8/caching/dependencies/CExpressionDependency.php new file mode 100644 index 0000000..d512651 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CExpressionDependency.php @@ -0,0 +1,49 @@ +<?php +/** + * CExpressionDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CExpressionDependency represents a dependency based on the result of a PHP expression. + * + * CExpressionDependency performs dependency checking based on the + * result of a PHP {@link expression}. + * The dependency is reported as unchanged if and only if the result is + * the same as the one evaluated when storing the data to cache. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CExpressionDependency.php 883 2009-03-23 20:52:35Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CExpressionDependency extends CCacheDependency +{ + /** + * @var string the PHP expression whose result is used to determine the dependency. + */ + public $expression; + + /** + * Constructor. + * @param string the PHP expression whose result is used to determine the dependency. + */ + public function __construct($expression='true') + { + $this->expression=$expression; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the result of the PHP expression. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + return @eval('return '.$this->expression.';'); + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CFileCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CFileCacheDependency.php new file mode 100644 index 0000000..7161887 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CFileCacheDependency.php @@ -0,0 +1,53 @@ +<?php +/** + * CFileCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileCacheDependency represents a dependency based on a file's last modification time. + * + * CFileCacheDependency performs dependency checking based on the + * last modification time of the file specified via {@link fileName}. + * The dependency is reported as unchanged if and only if the file's + * last modification time remains unchanged. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileCacheDependency.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CFileCacheDependency extends CCacheDependency +{ + /** + * @var string the name of the file whose last modification time is used to + * check if the dependency has been changed. + */ + public $fileName; + + /** + * Constructor. + * @param string name of the file whose change is to be checked. + */ + public function __construct($fileName=null) + { + $this->fileName=$fileName; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the file's last modification time. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->fileName!==null) + return @filemtime($this->fileName); + else + throw new CException(Yii::t('yii','CFileCacheDependency.fileName cannot be empty.')); + } +} diff --git a/web/framework-1.0.8/caching/dependencies/CGlobalStateCacheDependency.php b/web/framework-1.0.8/caching/dependencies/CGlobalStateCacheDependency.php new file mode 100644 index 0000000..7e0b792 --- /dev/null +++ b/web/framework-1.0.8/caching/dependencies/CGlobalStateCacheDependency.php @@ -0,0 +1,54 @@ +<?php +/** + * CGlobalStateCacheDependency class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGlobalStateCacheDependency represents a dependency based on a global state value. + * + * CGlobalStateCacheDependency checks if a global state is changed or not. + * If the global state is changed, the dependency is reported as changed. + * To specify which global state this dependency should check with, + * set {@link stateName} to the name of the global state. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CGlobalStateCacheDependency.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.caching.dependencies + * @since 1.0 + */ +class CGlobalStateCacheDependency extends CCacheDependency +{ + /** + * @var string the name of the global state whose value is to check + * if the dependency has changed. + * @see CApplication::setGlobalState + */ + public $stateName; + + /** + * Constructor. + * @param string the name of the global state + */ + public function __construct($name=null) + { + $this->stateName=$name; + } + + /** + * Generates the data needed to determine if dependency has been changed. + * This method returns the value of the global state. + * @return mixed the data needed to determine if dependency has been changed. + */ + protected function generateDependentData() + { + if($this->stateName!==null) + return Yii::app()->getGlobalState($this->stateName); + else + throw new CException(Yii::t('yii','CGlobalStateCacheDependency.stateName cannot be empty.')); + } +} diff --git a/web/framework-1.0.8/collections/CAttributeCollection.php b/web/framework-1.0.8/collections/CAttributeCollection.php new file mode 100644 index 0000000..c921e42 --- /dev/null +++ b/web/framework-1.0.8/collections/CAttributeCollection.php @@ -0,0 +1,187 @@ +<?php +/** + * This file contains classes implementing attribute collection feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CAttributeCollection implements a collection for storing attribute names and values. + * + * Besides all functionalities provided by {@link CMap}, CAttributeCollection + * allows you to get and set attribute values like getting and setting + * properties. For example, the following usages are all valid for a + * CAttributeCollection object: + * <pre> + * $collection->text='text'; // same as: $collection->add('text','text'); + * echo $collection->text; // same as: echo $collection->itemAt('text'); + * </pre> + * + * The case sensitivity of attribute names can be toggled by setting the + * {@link caseSensitive} property of the collection. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CAttributeCollection.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CAttributeCollection extends CMap +{ + /** + * @var boolean whether the keys are case-sensitive. Defaults to false. + */ + public $caseSensitive=false; + + /** + * Returns a property value or an event handler list by property or event name. + * This method overrides the parent implementation by returning + * a key value if the key exists in the collection. + * @param string the property name or the event name + * @return mixed the property value or the event handler list + * @throws CException if the property/event is not defined. + */ + public function __get($name) + { + if($this->contains($name)) + return $this->itemAt($name); + else + return parent::__get($name); + } + + /** + * Sets value of a component property. + * This method overrides the parent implementation by adding a new key value + * to the collection. + * @param string the property name or event name + * @param mixed the property value or event handler + * @throws CException If the property is not defined or read-only. + */ + public function __set($name,$value) + { + $this->add($name,$value); + } + + /** + * Checks if a property value is null. + * This method overrides the parent implementation by checking + * if the key exists in the collection and contains a non-null value. + * @param string the property name or the event name + * @return boolean whether the property value is null + * @since 1.0.1 + */ + public function __isset($name) + { + if($this->contains($name)) + return $this->itemAt($name)!==null; + else + return parent::__isset($name); + } + + /** + * Sets a component property to be null. + * This method overrides the parent implementation by clearing + * the specified key value. + * @param string the property name or the event name + * @since 1.0.1 + */ + public function __unset($name) + { + $this->remove($name); + } + + /** + * Returns the item with the specified key. + * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false. + * @param mixed the key + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key) + { + if($this->caseSensitive) + return parent::itemAt($key); + else + return parent::itemAt(strtolower($key)); + } + + /** + * Adds an item into the map. + * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false. + * @param mixed key + * @param mixed value + */ + public function add($key,$value) + { + if($this->caseSensitive) + parent::add($key,$value); + else + parent::add(strtolower($key),$value); + } + + /** + * Removes an item from the map by its key. + * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false. + * @param mixed the key of the item to be removed + * @return mixed the removed value, null if no such key exists. + */ + public function remove($key) + { + if($this->caseSensitive) + return parent::remove($key); + else + return parent::remove(strtolower($key)); + } + + /** + * Returns whether the specified is in the map. + * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false. + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + if($this->caseSensitive) + return parent::contains($key); + else + return parent::contains(strtolower($key)); + } + + /** + * Determines whether a property is defined. + * This method overrides parent implementation by returning true + * if the collection contains the named key. + * @param string the property name + * @return boolean whether the property is defined + */ + public function hasProperty($name) + { + return $this->contains($name) || parent::hasProperty($name); + } + + /** + * Determines whether a property can be read. + * This method overrides parent implementation by returning true + * if the collection contains the named key. + * @param string the property name + * @return boolean whether the property can be read + */ + public function canGetProperty($name) + { + return $this->contains($name) || parent::canGetProperty($name); + } + + /** + * Determines whether a property can be set. + * This method overrides parent implementation by always returning true + * because you can always add a new value to the collection. + * @param string the property name + * @return boolean true + */ + public function canSetProperty($name) + { + return true; + } +} diff --git a/web/framework-1.0.8/collections/CConfiguration.php b/web/framework-1.0.8/collections/CConfiguration.php new file mode 100644 index 0000000..5edf854 --- /dev/null +++ b/web/framework-1.0.8/collections/CConfiguration.php @@ -0,0 +1,121 @@ +<?php +/** + * This file contains classes implementing configuration feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CConfiguration represents an array-based configuration. + * + * It can be used to initialize an object's properties. + * + * The configuration data may be obtained from a PHP script. For example, + * <pre> + * <?php + * return array + * ( + * 'name'=>'My Application', + * 'defaultController'=>'index', + * ); + * ?> + * </pre> + * Use the following code to load the above configuration data: + * <pre> + * $config=new CConfiguration('path/to/config.php'); + * </pre> + * + * To apply the configuration to an object, call {@link applyTo()}. + * Each (key,value) pair in the configuration data is applied + * to the object like: $object->$key=$value. + * + * Since CConfiguration extends from {@link CMap}, it can be + * used like an associative array. See {@link CMap} for more details. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CConfiguration.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CConfiguration extends CMap +{ + /** + * Constructor. + * @param mixed if string, it represents a config file (a PHP script returning the configuration as an array); + * If array, it is config data. + */ + public function __construct($data=null) + { + if(is_string($data)) + parent::__construct(require($data)); + else + parent::__construct($data); + } + + /** + * Loads configuration data from a file and merges it with the existing configuration. + * + * A config file must be a PHP script returning a configuration array (like the following) + * <pre> + * return array + * ( + * 'name'=>'My Application', + * 'defaultController'=>'index', + * ); + * </pre> + * + * @param string configuration file path (if using relative path, be aware of what is the current path) + * @see mergeWith + */ + public function loadFromFile($configFile) + { + $data=require($configFile); + if($this->getCount()>0) + $this->mergeWith($data); + else + $this->copyFrom($data); + } + + /** + * Saves the configuration into a string. + * The string is a valid PHP expression representing the configuration data as an array. + * @return string the string representation of the configuration + */ + public function saveAsString() + { + return str_replace("\r",'',var_export($this->toArray(),true)); + } + + /** + * Applies the configuration to an object. + * Each (key,value) pair in the configuration data is applied + * to the object like: $object->$key=$value. + * @param object object to be applied with this configuration + */ + public function applyTo($object) + { + foreach($this->toArray() as $key=>$value) + $object->$key=$value; + } + + /** + * Creates an object and initializes it based on the given configuration. + * + * NOTE: this method has been deprecated since version 1.0.1. + * Please use {@link YiiBase::createComponent Yii::createComponent}, instead. + * + * @param mixed the configuration. It can be either a string or an array. + * @return mixed the created object + * @throws CException if the configuration does not have 'class' value + */ + public static function createObject($config) + { + if($config instanceof self) + $config=$config->toArray(); + return Yii::createComponent($config); + } +} diff --git a/web/framework-1.0.8/collections/CList.php b/web/framework-1.0.8/collections/CList.php new file mode 100644 index 0000000..f14feb6 --- /dev/null +++ b/web/framework-1.0.8/collections/CList.php @@ -0,0 +1,341 @@ +<?php +/** + * This file contains classes implementing list feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CList implements an integer-indexed collection class. + * + * You can access, append, insert, remove an item by using + * {@link itemAt}, {@link add}, {@link insertAt}, {@link remove}, and {@link removeAt}. + * To get the number of the items in the list, use {@link getCount}. + * CList can also be used like a regular array as follows, + * <pre> + * $list[]=$item; // append at the end + * $list[$index]=$item; // $index must be between 0 and $list->Count + * unset($list[$index]); // remove the item at $index + * if(isset($list[$index])) // if the list has an item at $index + * foreach($list as $index=>$item) // traverse each item in the list + * $n=count($list); // returns the number of items in the list + * </pre> + * + * To extend CList by doing additional operations with each addition or removal + * operation (e.g. performing type check), override {@link insertAt()}, and {@link removeAt()}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CList.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CList extends CComponent implements IteratorAggregate,ArrayAccess,Countable +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var integer number of items + */ + private $_c=0; + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + + /** + * Constructor. + * Initializes the list with an array or an iterable object. + * @param array the initial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @throws CException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + + /** + * @return boolean whether this list is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=$value; + } + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new CListIterator($this->_d); + } + + /** + * Returns the number of items in the list. + * This method is required by Countable interface. + * @return integer number of items in the list. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the list + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the item at the specified offset. + * This method is exactly the same as {@link offsetGet}. + * @param integer the index of the item + * @return mixed the item at the index + * @throws CException if the index is out of the range + */ + public function itemAt($index) + { + if(isset($this->_d[$index])) + return $this->_d[$index]; + else if($index>=0 && $index<$this->_c) // in case the value is null + return $this->_d[$index]; + else + throw new CException(Yii::t('yii','List index "{index}" is out of bound.', + array('{index}'=>$index))); + } + + /** + * Appends an item at the end of the list. + * @param mixed new item + * @return integer the zero-based index at which the item is added + */ + public function add($item) + { + $this->insertAt($this->_c,$item); + return $this->_c-1; + } + + /** + * Inserts an item at the specified position. + * Original item at the position and the next items + * will be moved one step towards the end. + * @param integer the specified position. + * @param mixed new item + * @throws CException If the index specified exceeds the bound or the list is read-only + */ + public function insertAt($index,$item) + { + if(!$this->_r) + { + if($index===$this->_c) + $this->_d[$this->_c++]=$item; + else if($index>=0 && $index<$this->_c) + { + array_splice($this->_d,$index,0,array($item)); + $this->_c++; + } + else + throw new CException(Yii::t('yii','List index "{index}" is out of bound.', + array('{index}'=>$index))); + } + else + throw new CException(Yii::t('yii','The list is read only.')); + } + + /** + * Removes an item from the list. + * The list will first search for the item. + * The first item found will be removed from the list. + * @param mixed the item to be removed. + * @return integer the index at which the item is being removed + * @throws CException If the item does not exist + */ + public function remove($item) + { + if(($index=$this->indexOf($item))>=0) + { + $this->removeAt($index); + return $index; + } + else + return false; + } + + /** + * Removes an item at the specified position. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + * @throws CException If the index specified exceeds the bound or the list is read-only + */ + public function removeAt($index) + { + if(!$this->_r) + { + if($index>=0 && $index<$this->_c) + { + $this->_c--; + if($index===$this->_c) + return array_pop($this->_d); + else + { + $item=$this->_d[$index]; + array_splice($this->_d,$index,1); + return $item; + } + } + else + throw new CException(Yii::t('yii','List index "{index}" is out of bound.', + array('{index}'=>$index))); + } + else + throw new CException(Yii::t('yii','The list is read only.')); + } + + /** + * Removes all items in the list. + */ + public function clear() + { + for($i=$this->_c-1;$i>=0;--$i) + $this->removeAt($i); + } + + /** + * @param mixed the item + * @return boolean whether the list contains the item + */ + public function contains($item) + { + return $this->indexOf($item)>=0; + } + + /** + * @param mixed the item + * @return integer the index of the item in the list (0 based), -1 if not found. + */ + public function indexOf($item) + { + if(($index=array_search($item,$this->_d,true))!==false) + return $index; + else + return -1; + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the list. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws CException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + if($this->_c>0) + $this->clear(); + if($data instanceof CList) + $data=$data->_d; + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.')); + } + + /** + * Merges iterable data into the map. + * New data will be appended to the end of the existing data. + * @param mixed the data to be merged with, must be an array or object implementing Traversable + * @throws CException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + if($data instanceof CList) + $data=$data->_d; + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.')); + } + + /** + * Returns whether there is an item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return ($offset>=0 && $offset<$this->_c); + } + + /** + * Returns the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve item. + * @return mixed the item at the offset + * @throws CException if the offset is invalid + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set item + * @param mixed the item value + */ + public function offsetSet($offset,$item) + { + if($offset===null || $offset===$this->_c) + $this->insertAt($this->_c,$item); + else + { + $this->removeAt($offset); + $this->insertAt($offset,$item); + } + } + + /** + * Unsets the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to unset item + */ + public function offsetUnset($offset) + { + $this->removeAt($offset); + } +} + diff --git a/web/framework-1.0.8/collections/CListIterator.php b/web/framework-1.0.8/collections/CListIterator.php new file mode 100644 index 0000000..f62d938 --- /dev/null +++ b/web/framework-1.0.8/collections/CListIterator.php @@ -0,0 +1,94 @@ +<?php +/** + * CListIterator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CListIterator implements an interator for {@link CList}. + * + * It allows CList to return a new iterator for traversing the items in the list. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CListIterator.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CListIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} diff --git a/web/framework-1.0.8/collections/CMap.php b/web/framework-1.0.8/collections/CMap.php new file mode 100644 index 0000000..90f9ac0 --- /dev/null +++ b/web/framework-1.0.8/collections/CMap.php @@ -0,0 +1,319 @@ +<?php +/** + * This file contains classes implementing Map feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMap implements a collection that takes key-value pairs. + * + * You can access, add or remove an item with a key by using + * {@link itemAt}, {@link add}, and {@link remove}. + * To get the number of the items in the map, use {@link getCount}. + * CMap can also be used like a regular array as follows, + * <pre> + * $map[$key]=$value; // add a key-value pair + * unset($map[$key]); // remove the value with the specified key + * if(isset($map[$key])) // if the map contains the key + * foreach($map as $key=>$value) // traverse the items in the map + * $n=count($map); // returns the number of items in the map + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMap.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CMap extends CComponent implements IteratorAggregate,ArrayAccess,Countable +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + + /** + * Constructor. + * Initializes the list with an array or an iterable object. + * @param array the intial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @throws CException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + + /** + * @return boolean whether this map is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=$value; + } + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return CMapIterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new CMapIterator($this->_d); + } + + /** + * Returns the number of items in the map. + * This method is required by Countable interface. + * @return integer number of items in the map. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the map + */ + public function getCount() + { + return count($this->_d); + } + + /** + * @return array the key list + */ + public function getKeys() + { + return array_keys($this->_d); + } + + /** + * Returns the item with the specified key. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the key + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key) + { + if(isset($this->_d[$key])) + return $this->_d[$key]; + else + return null; + } + + /** + * Adds an item into the map. + * Note, if the specified key already exists, the old value will be overwritten. + * @param mixed key + * @param mixed value + * @throws CException if the map is read-only + */ + public function add($key,$value) + { + if(!$this->_r) + $this->_d[$key]=$value; + else + throw new CException(Yii::t('yii','The map is read only.')); + } + + /** + * Removes an item from the map by its key. + * @param mixed the key of the item to be removed + * @return mixed the removed value, null if no such key exists. + * @throws CException if the map is read-only + */ + public function remove($key) + { + if(!$this->_r) + { + if(isset($this->_d[$key])) + { + $value=$this->_d[$key]; + unset($this->_d[$key]); + return $value; + } + else + { + // it is possible the value is null, which is not detected by isset + unset($this->_d[$key]); + return null; + } + } + else + throw new CException(Yii::t('yii','The map is read only.')); + } + + /** + * Removes all items in the map. + */ + public function clear() + { + foreach(array_keys($this->_d) as $key) + $this->remove($key); + } + + /** + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + return isset($this->_d[$key]) || array_key_exists($key,$this->_d); + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the map. + * Note, existing data in the map will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws CException If data is neither an array nor an iterator. + */ + public function copyFrom($data) + { + if(is_array($data) || $data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + if($data instanceof CMap) + $data=$data->_d; + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new CException(Yii::t('yii','Map data must be an array or an object implementing Traversable.')); + } + + /** + * Merges iterable data into the map. + * + * Existing elements in the map will be overwritten if their keys are the same as those in the source. + * If the merge is recursive, the following algorithm is performed: + * <ul> + * <li>the map data is saved as $a, and the source data is saved as $b;</li> + * <li>if $a and $b both have an array indxed at the same string key, the arrays will be merged using this algorithm;</li> + * <li>any integer-indexed elements in $b will be appended to $a and reindxed accordingly;</li> + * <li>any string-indexed elements in $b will overwrite elements in $a with the same index;</li> + * </ul> + * + * @param mixed the data to be merged with, must be an array or object implementing Traversable + * @param boolean whether the merging should be recursive. + * + * @throws CException If data is neither an array nor an iterator. + */ + public function mergeWith($data,$recursive=true) + { + if(is_array($data) || $data instanceof Traversable) + { + if($data instanceof CMap) + $data=$data->_d; + if($recursive) + { + if($data instanceof Traversable) + { + $d=array(); + foreach($data as $key=>$value) + $d[$key]=$value; + $this->_d=self::mergeArray($this->_d,$d); + } + else + $this->_d=self::mergeArray($this->_d,$data); + } + else + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + } + else if($data!==null) + throw new CException(Yii::t('yii','Map data must be an array or an object implementing Traversable.')); + } + + /** + * Merges two arrays into one recursively. + * @param array array to be merged to + * @param array array to be merged from + * @return array the merged array (the original arrays are not changed.) + * @see mergeWith + */ + public static function mergeArray($a,$b) + { + foreach($b as $k=>$v) + { + if(is_integer($k)) + $a[]=$v; + else if(is_array($v) && isset($a[$k]) && is_array($a[$k])) + $a[$k]=self::mergeArray($a[$k],$v); + else + $a[$k]=$v; + } + return $a; + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } +} diff --git a/web/framework-1.0.8/collections/CMapIterator.php b/web/framework-1.0.8/collections/CMapIterator.php new file mode 100644 index 0000000..d96dafd --- /dev/null +++ b/web/framework-1.0.8/collections/CMapIterator.php @@ -0,0 +1,93 @@ +<?php +/** + * CMapIterator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMapIterator implements an interator for {@link CMap}. + * + * It allows CMap to return a new iterator for traversing the items in the map. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMapIterator.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CMapIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_keys=array_keys($data); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key=reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return $this->_d[$this->_key]; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_key=next($this->_keys); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key!==false; + } +} diff --git a/web/framework-1.0.8/collections/CQueue.php b/web/framework-1.0.8/collections/CQueue.php new file mode 100644 index 0000000..588d905 --- /dev/null +++ b/web/framework-1.0.8/collections/CQueue.php @@ -0,0 +1,168 @@ +<?php +/** + * This file contains classes implementing the queue feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CQueue implements a queue. + * + * The typical queue operations are implemented, which include + * {@link enqueue()}, {@link dequeue()} and {@link peek()}. In addition, + * {@link contains()} can be used to check if an item is contained + * in the queue. To obtain the number of the items in the queue, + * check the {@link getCount Count} property. + * + * Items in the queue may be traversed using foreach as follows, + * <pre> + * foreach($queue as $item) ... + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CQueue.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CQueue extends CComponent implements IteratorAggregate,Countable +{ + /** + * internal data storage + * @var array + */ + private $_d=array(); + /** + * number of items + * @var integer + */ + private $_c=0; + + /** + * Constructor. + * Initializes the queue with an array or an iterable object. + * @param array the intial data. Default is null, meaning no initialization. + * @throws CException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null) + { + if($data!==null) + $this->copyFrom($data); + } + + /** + * @return array the list of items in queue + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the queue. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws CException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + $this->clear(); + foreach($data as $item) + { + $this->_d[]=$item; + ++$this->_c; + } + } + else if($data!==null) + throw new CException(Yii::t('yii','Queue data must be an array or an object implementing Traversable.')); + } + + /** + * Removes all items in the queue. + */ + public function clear() + { + $this->_c=0; + $this->_d=array(); + } + + /** + * @param mixed the item + * @return boolean whether the queue contains the item + */ + public function contains($item) + { + return array_search($item,$this->_d,true)!==false; + } + + /** + * Returns the item at the top of the queue. + * @return mixed item at the top of the queue + * @throws CException if the queue is empty + */ + public function peek() + { + if($this->_c===0) + throw new CException(Yii::t('yii','The queue is empty.')); + else + return $this->_d[$this->_c-1]; + } + + /** + * Removes and returns the object at the beginning of the queue. + * @return mixed the item at the beginning of the queue + * @throws CException if the queue is empty + */ + public function dequeue() + { + if($this->_c===0) + throw new CException(Yii::t('yii','The queue is empty.')); + else + { + --$this->_c; + return array_shift($this->_d); + } + } + + /** + * Adds an object to the end of the queue. + * @param mixed the item to be appended into the queue + */ + public function enqueue($item) + { + ++$this->_c; + array_push($this->_d,$item); + } + + /** + * Returns an iterator for traversing the items in the queue. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the queue. + */ + public function getIterator() + { + return new CQueueIterator($this->_d); + } + + /** + * @return integer the number of items in the queue + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the number of items in the queue. + * This method is required by Countable interface. + * @return integer number of items in the queue. + */ + public function count() + { + return $this->getCount(); + } +} diff --git a/web/framework-1.0.8/collections/CQueueIterator.php b/web/framework-1.0.8/collections/CQueueIterator.php new file mode 100644 index 0000000..d897d15 --- /dev/null +++ b/web/framework-1.0.8/collections/CQueueIterator.php @@ -0,0 +1,94 @@ +<?php +/** + * CQueueIterator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CQueueIterator implements an interator for {@link CQueue}. + * + * It allows CQueue to return a new iterator for traversing the items in the queue. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CQueueIterator.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CQueueIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} diff --git a/web/framework-1.0.8/collections/CStack.php b/web/framework-1.0.8/collections/CStack.php new file mode 100644 index 0000000..69eaeaf --- /dev/null +++ b/web/framework-1.0.8/collections/CStack.php @@ -0,0 +1,169 @@ +<?php +/** + * This file contains classes implementing the stack feature. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CStack implements a stack. + * + * The typical stack operations are implemented, which include + * {@link push()}, {@link pop()} and {@link peek()}. In addition, + * {@link contains()} can be used to check if an item is contained + * in the stack. To obtain the number of the items in the stack, + * check the {@link getCount Count} property. + * + * Items in the stack may be traversed using foreach as follows, + * <pre> + * foreach($stack as $item) ... + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CStack.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CStack extends CComponent implements IteratorAggregate,Countable +{ + /** + * internal data storage + * @var array + */ + private $_d=array(); + /** + * number of items + * @var integer + */ + private $_c=0; + + /** + * Constructor. + * Initializes the stack with an array or an iterable object. + * @param array the initial data. Default is null, meaning no initialization. + * @throws CException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null) + { + if($data!==null) + $this->copyFrom($data); + } + + /** + * @return array the list of items in stack + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the stack. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws CException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + $this->clear(); + foreach($data as $item) + { + $this->_d[]=$item; + ++$this->_c; + } + } + else if($data!==null) + throw new CException(Yii::t('yii','Stack data must be an array or an object implementing Traversable.')); + } + + /** + * Removes all items in the stack. + */ + public function clear() + { + $this->_c=0; + $this->_d=array(); + } + + /** + * @param mixed the item + * @return boolean whether the stack contains the item + */ + public function contains($item) + { + return array_search($item,$this->_d,true)!==false; + } + + /** + * Returns the item at the top of the stack. + * Unlike {@link pop()}, this method does not remove the item from the stack. + * @return mixed item at the top of the stack + * @throws CException if the stack is empty + */ + public function peek() + { + if($this->_c) + return $this->_d[$this->_c-1]; + else + throw new CException(Yii::t('yii','The stack is empty.')); + } + + /** + * Pops up the item at the top of the stack. + * @return mixed the item at the top of the stack + * @throws CException if the stack is empty + */ + public function pop() + { + if($this->_c) + { + --$this->_c; + return array_pop($this->_d); + } + else + throw new CException(Yii::t('yii','The stack is empty.')); + } + + /** + * Pushes an item into the stack. + * @param mixed the item to be pushed into the stack + */ + public function push($item) + { + ++$this->_c; + array_push($this->_d,$item); + } + + /** + * Returns an iterator for traversing the items in the stack. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the stack. + */ + public function getIterator() + { + return new CStackIterator($this->_d); + } + + /** + * @return integer the number of items in the stack + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the number of items in the stack. + * This method is required by Countable interface. + * @return integer number of items in the stack. + */ + public function count() + { + return $this->getCount(); + } +} diff --git a/web/framework-1.0.8/collections/CStackIterator.php b/web/framework-1.0.8/collections/CStackIterator.php new file mode 100644 index 0000000..0af191f --- /dev/null +++ b/web/framework-1.0.8/collections/CStackIterator.php @@ -0,0 +1,94 @@ +<?php +/** + * CStackIterator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CStackIterator implements an interator for {@link CStack}. + * + * It allows CStack to return a new iterator for traversing the items in the stack. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CStackIterator.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CStackIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} diff --git a/web/framework-1.0.8/collections/CTypedList.php b/web/framework-1.0.8/collections/CTypedList.php new file mode 100644 index 0000000..f443657 --- /dev/null +++ b/web/framework-1.0.8/collections/CTypedList.php @@ -0,0 +1,52 @@ +<?php +/** + * This file contains CTypedList class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTypedList represents a list whose items are of the certain type. + * + * CTypedList extends {@link CList} by making sure that the elements to be + * added to the list is of certain class type. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CTypedList.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.collections + * @since 1.0 + */ +class CTypedList extends CList +{ + private $_type; + + /** + * Constructor. + * @param string class type + */ + public function __construct($type) + { + $this->_type=$type; + } + + /** + * Inserts an item at the specified position. + * This method overrides the parent implementation by + * checking the item to be inserted is of certain type. + * @param integer the specified position. + * @param mixed new item + * @throws CException If the index specified exceeds the bound, + * the list is read-only or the element is not of the expected type. + */ + public function insertAt($index,$item) + { + if($item instanceof $this->_type) + parent::insertAt($index,$item); + else + throw new CException(Yii::t('yii','CTypedList<{type}> can only hold objects of {type} class.', + array('{type}'=>$this->_type))); + } +} diff --git a/web/framework-1.0.8/db/CDbCommand.php b/web/framework-1.0.8/db/CDbCommand.php new file mode 100644 index 0000000..e0b1c3b --- /dev/null +++ b/web/framework-1.0.8/db/CDbCommand.php @@ -0,0 +1,326 @@ +<?php +/** + * This file contains the CDbCommand class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCommand represents an SQL statement to execute against a database. + * + * It is usually created by calling {@link CDbConnection::createCommand}. + * The SQL statement to be executed may be set via {@link setText Text}. + * + * To execute a non-query SQL (such as insert, delete, update), call + * {@link execute}. To execute an SQL statement that returns result data set + * (such as SELECT), use {@link query} or its convenient versions {@link queryRow}, + * {@link queryColumn}, or {@link queryScalar}. + * + * If an SQL statement returns results (such as a SELECT SQL), the results + * can be accessed via the returned {@link CDbDataReader}. + * + * CDbCommand supports SQL statment preparation and parameter binding. + * Call {@link bindParam} to bind a PHP variable to a parameter in SQL. + * Call {@link bindValue} to bind a value to an SQL parameter. + * When binding a parameter, the SQL statement is automatically prepared. + * You may also call {@link prepare} to explicitly prepare an SQL statement. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbCommand.php 1215 2009-07-06 12:55:11Z qiang.xue $ + * @package system.db + * @since 1.0 + */ +class CDbCommand extends CComponent +{ + private $_connection; + private $_text=''; + private $_statement=null; + private $_params; + + /** + * Constructor. + * @param CDbConnection the database connection + * @param string the SQL statement to be executed + */ + public function __construct(CDbConnection $connection,$text) + { + $this->_connection=$connection; + $this->setText($text); + } + + /** + * Set the statement to null when serializing. + */ + public function __sleep() + { + $this->_statement=null; + return array_keys(get_object_vars($this)); + } + + /** + * @return string the SQL statement to be executed + */ + public function getText() + { + return $this->_text; + } + + /** + * Specifies the SQL statement to be executed. + * Any previous execution will be terminated or cancel. + * @param string the SQL statement to be executed + */ + public function setText($value) + { + $this->_text=$value; + $this->cancel(); + } + + /** + * @return CDbConnection the connection associated with this command + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return PDOStatement the underlying PDOStatement for this command + * It could be null if the statement is not prepared yet. + */ + public function getPdoStatement() + { + return $this->_statement; + } + + /** + * Prepares the SQL statement to be executed. + * For complex SQL statement that is to be executed multiple times, + * this may improve performance. + * For SQL statement with binding parameters, this method is invoked + * automatically. + */ + public function prepare() + { + if($this->_statement==null) + { + try + { + $this->_statement=$this->getConnection()->getPdoInstance()->prepare($this->getText()); + $this->_params=array(); + } + catch(Exception $e) + { + Yii::log('Error in preparing SQL: '.$this->getText(),CLogger::LEVEL_ERROR,'system.db.CDbCommand'); + throw new CDbException(Yii::t('yii','CDbCommand failed to prepare the SQL statement: {error}', + array('{error}'=>$e->getMessage()))); + } + } + } + + /** + * Cancels the execution of the SQL statement. + */ + public function cancel() + { + $this->_statement=null; + } + + /** + * Binds a parameter to the SQL statement to be executed. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * @param mixed Name of the PHP variable to bind to the SQL statement parameter + * @param int SQL data type of the parameter. If null, the type is determined by the PHP type of the value. + * @param int length of the data type + * @return CDbCommand the current command being executed (this is available since version 1.0.8) + * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php + */ + public function bindParam($name, &$value, $dataType=null, $length=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindParam($name,$value,$this->_connection->getPdoType(gettype($value))); + else if($length===null) + $this->_statement->bindParam($name,$value,$dataType); + else + $this->_statement->bindParam($name,$value,$dataType,$length); + if($this->_connection->enableParamLogging) + $this->_params[]=$name.'=['.gettype($value).']'; + return $this; + } + + /** + * Binds a value to a parameter. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * @param mixed The value to bind to the parameter + * @param int SQL data type of the parameter. If null, the type is determined by the PHP type of the value. + * @return CDbCommand the current command being executed (this is available since version 1.0.8) + * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php + */ + public function bindValue($name, $value, $dataType=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindValue($name,$value,$this->_connection->getPdoType(gettype($value))); + else + $this->_statement->bindValue($name,$value,$dataType); + if($this->_connection->enableParamLogging) + $this->_params[]=$name.'='.var_export($value,true); + return $this; + } + + /** + * Executes the SQL statement. + * This method is meant only for executing non-query SQL statement. + * No result set will be returned. + * @return integer number of rows affected by the execution. + * @throws CException execution failed + */ + public function execute() + { + $params=$this->_connection->enableParamLogging && !empty($this->_params) ? '. Bind with parameter ' . implode(', ',$this->_params) : ''; + Yii::trace('Executing SQL: '.$this->getText().$params,'system.db.CDbCommand'); + try + { + if($this->_connection->enableProfiling) + Yii::beginProfile('system.db.CDbCommand.execute('.$this->getText().')','system.db.CDbCommand.execute'); + + if($this->_statement instanceof PDOStatement) + { + $this->_statement->execute(); + $n=$this->_statement->rowCount(); + } + else + $n=$this->getConnection()->getPdoInstance()->exec($this->getText()); + + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.execute('.$this->getText().')','system.db.CDbCommand.execute'); + + return $n; + } + catch(Exception $e) + { + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.execute('.$this->getText().')','system.db.CDbCommand.execute'); + Yii::log('Error in executing SQL: '.$this->getText().$params,CLogger::LEVEL_ERROR,'system.db.CDbCommand'); + throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}', + array('{error}'=>$e->getMessage()))); + } + } + + /** + * Executes the SQL statement and returns query result. + * This method is for executing an SQL query that returns result set. + * @return CDbDataReader the reader object for fetching the query result + * @throws CException execution failed + */ + public function query() + { + return $this->queryInternal('',0); + } + + /** + * Executes the SQL statement and returns all rows. + * @param boolean whether each row should be returned as an associated array with + * column names as the keys or the array keys are column indexes (0-based). + * @return array all rows of the query result. Each array element is an array representing a row. + * An empty array is returned if the query results in nothing. + * @throws CException execution failed + */ + public function queryAll($fetchAssociative=true) + { + return $this->queryInternal('fetchAll',$fetchAssociative ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); + } + + /** + * Executes the SQL statement and returns the first row of the result. + * This is a convenient method of {@link query} when only the first row of data is needed. + * @param boolean whether the row should be returned as an associated array with + * column names as the keys or the array keys are column indexes (0-based). + * @return array the first row of the query result, false if no result. + * @throws CException execution failed + */ + public function queryRow($fetchAssociative=true) + { + return $this->queryInternal('fetch',$fetchAssociative ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); + } + + /** + * Executes the SQL statement and returns the value of the first column in the first row of data. + * This is a convenient method of {@link query} when only a single scalar + * value is needed (e.g. obtaining the count of the records). + * @return mixed the value of the first column in the first row of the query result. False is returned if there is no value. + * @throws CException execution failed + */ + public function queryScalar() + { + $result=$this->queryInternal('fetchColumn',0); + if(is_resource($result) && get_resource_type($result)==='stream') + return stream_get_contents($result); + else + return $result; + } + + /** + * Executes the SQL statement and returns the first column of the result. + * This is a convenient method of {@link query} when only the first column of data is needed. + * Note, the column returned will contain the first element in each row of result. + * @return array the first column of the query result. Empty array if no result. + * @throws CException execution failed + */ + public function queryColumn() + { + return $this->queryInternal('fetchAll',PDO::FETCH_COLUMN); + } + + /** + * @param string method of PDOStatement to be called + * @param mixed the first parameter to be passed to the method + * @return mixed the method execution result + */ + private function queryInternal($method,$mode) + { + $params=$this->_connection->enableParamLogging && !empty($this->_params) ? '. Bind with parameter ' . implode(', ',$this->_params) : ''; + Yii::trace('Querying SQL: '.$this->getText().$params,'system.db.CDbCommand'); + try + { + if($this->_connection->enableProfiling) + Yii::beginProfile('system.db.CDbCommand.query('.$this->getText().')','system.db.CDbCommand.query'); + + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + + if($method==='') + $result=new CDbDataReader($this); + else + { + $result=$this->_statement->{$method}($mode); + $this->_statement->closeCursor(); + } + + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.query('.$this->getText().')','system.db.CDbCommand.query'); + + return $result; + } + catch(Exception $e) + { + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.query('.$this->getText().')','system.db.CDbCommand.query'); + Yii::log('Error in querying SQL: '.$this->getText().$params,CLogger::LEVEL_ERROR,'system.db.CDbCommand'); + throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}', + array('{error}'=>$e->getMessage()))); + } + } +} diff --git a/web/framework-1.0.8/db/CDbConnection.php b/web/framework-1.0.8/db/CDbConnection.php new file mode 100644 index 0000000..4329bad --- /dev/null +++ b/web/framework-1.0.8/db/CDbConnection.php @@ -0,0 +1,645 @@ +<?php +/** + * CDbConnection class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbConnection represents a connection to a database. + * + * CDbConnection works together with {@link CDbCommand}, {@link CDbDataReader} + * and {@link CDbTransaction} to provide data access to various DBMS + * in a common set of APIs. They are a thin wrapper of the {@link http://www.php.net/manual/en/ref.pdo.php PDO} + * PHP extension. + * + * To establish a connection, set {@link setActive active} to true after + * specifying {@link connectionString}, {@link username} and {@link password}. + * + * The following example shows how to create a CDbConnection instance and establish + * the actual connection: + * <pre> + * $connection=new CDbConnection($dsn,$username,$password); + * $connection->active=true; + * </pre> + * + * After the DB connection is established, one can execute an SQL statement like the following: + * <pre> + * $command=$connection->createCommand($sqlStatement); + * $command->execute(); // a non-query SQL statement execution + * // or execute an SQL query and fetch the result set + * $reader=$command->query(); + * + * // each $row is an array representing a row of data + * foreach($reader as $row) ... + * </pre> + * + * One can do prepared SQL execution and bind parameters to the prepared SQL: + * <pre> + * $command=$connection->createCommand($sqlStatement); + * $command->bindParam($name1,$value1); + * $command->bindParam($name2,$value2); + * $command->execute(); + * </pre> + * + * To use transaction, do like the following: + * <pre> + * $transaction=$connection->beginTransaction(); + * try + * { + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * </pre> + * + * CDbConnection also provides a set of methods to support setting and querying + * of certain DBMS attributes, such as {@link getNullConversion nullConversion}. + * + * Since CDbConnection implements the interface IApplicationComponent, it can + * be used as an application component and be configured in application configuration, + * like the following, + * <pre> + * array( + * 'components'=>array( + * 'db'=>array( + * 'class'=>'CDbConnection', + * 'connectionString'=>'sqlite:path/to/dbfile', + * ), + * ), + * ) + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbConnection.php 1218 2009-07-07 17:30:18Z qiang.xue $ + * @package system.db + * @since 1.0 + */ +class CDbConnection extends CApplicationComponent +{ + /** + * @var string The Data Source Name, or DSN, contains the information required to connect to the database. + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public $connectionString; + /** + * @var string the username for establishing DB connection. Defaults to empty string. + */ + public $username=''; + /** + * @var string the password for establishing DB connection. Defaults to empty string. + */ + public $password=''; + /** + * @var integer number of seconds that table metadata can remain valid in cache. + * Use 0 or negative value to indicate not caching schema. + * If greater than 0 and the primary cache is enabled, the table metadata will be cached. + * @see schemaCachingExclude + */ + public $schemaCachingDuration=0; + /** + * @var array list of tables whose metadata should NOT be cached. Defaults to empty array. + * @see schemaCachingDuration + */ + public $schemaCachingExclude=array(); + /** + * @var boolean whether the database connection should be automatically established + * the component is being initialized. Defaults to true. Note, this property is only + * effective when the CDbConnection object is used as an application component. + */ + public $autoConnect=true; + /** + * @var string the charset used for database connection. The property is only used + * for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset + * as specified by the database. + */ + public $charset; + /** + * @var boolean whether to turn on prepare emulation. Defaults to false, meaning PDO + * will use the native prepare support if available. For some databases (such as MySQL), + * this may need to be set true so that PDO can emulate the prepare support to bypass + * the buggy native prepare support. Note, this property is only effective for PHP 5.1.3 or above. + */ + public $emulatePrepare=false; + /** + * @var boolean whether to log the values that are bound to a prepare SQL statement. + * Defaults to false. During development, you may consider setting this property to true + * so that parameter values bound to SQL statements are logged for debugging purpose. + * You should be aware that logging parameter values could be expensive and have significant + * impact on the performance of your application. + * @since 1.0.5 + */ + public $enableParamLogging=false; + /** + * @var boolean whether to enable profiling the SQL statements being executed. + * Defaults to false. This should be mainly enabled and used during development + * to find out the bottleneck of SQL executions. + * @since 1.0.6 + */ + public $enableProfiling=false; + + private $_attributes=array(); + private $_active=false; + private $_pdo; + private $_transaction; + private $_schema; + + + /** + * Constructor. + * Note, the DB connection is not established when this connection + * instance is created. Set {@link setActive active} property to true + * to establish the connection. + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @param string The user name for the DSN string. + * @param string The password for the DSN string. + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function __construct($dsn='',$username='',$password='') + { + $this->connectionString=$dsn; + $this->username=$username; + $this->password=$password; + } + + /** + * Close the connection when serializing. + */ + public function __sleep() + { + $this->close(); + return array_keys(get_object_vars($this)); + } + + /** + * @return array list of available PDO drivers + * @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php + */ + public static function getAvailableDrivers() + { + return PDO::getAvailableDrivers(); + } + + /** + * Initializes the component. + * This method is required by {@link IApplicationComponent} and is invoked by application + * when the CDbConnection is used as an application component. + * If you override this method, make sure to call the parent implementation + * so that the component can be marked as initialized. + */ + public function init() + { + parent::init(); + if($this->autoConnect) + $this->setActive(true); + } + + /** + * @return boolean whether the DB connection is established + */ + public function getActive() + { + return $this->_active; + } + + /** + * Open or close the DB connection. + * @param boolean whether to open or close DB connection + * @throws CException if connection fails + */ + public function setActive($value) + { + if($value!=$this->_active) + { + if($value) + $this->open(); + else + $this->close(); + } + } + + /** + * Opens DB connection if it is currently not + * @throws CException if connection fails + */ + protected function open() + { + if($this->_pdo===null) + { + if(empty($this->connectionString)) + throw new CDbException(Yii::t('yii','CDbConnection.connectionString cannot be empty.')); + try + { + Yii::trace('Opening DB connection','system.db.CDbConnection'); + $this->_pdo=$this->createPdoInstance(); + $this->initConnection($this->_pdo); + $this->_active=true; + } + catch(PDOException $e) + { + throw new CDbException(Yii::t('yii','CDbConnection failed to open the DB connection: {error}', + array('{error}'=>$e->getMessage()))); + } + } + } + + /** + * Closes the currently active DB connection. + * It does nothing if the connection is already closed. + */ + protected function close() + { + Yii::trace('Closing DB connection','system.db.CDbConnection'); + $this->_pdo=null; + $this->_active=false; + $this->_schema=null; + } + + /** + * Creates the PDO instance. + * When some functionalities are missing in the pdo driver, we may use + * an adapter class to provides them. + * @return PDO the pdo instance + * @since 1.0.4 + */ + protected function createPdoInstance() + { + $pdoClass='PDO'; + if(($pos=strpos($this->connectionString,':'))!==false) + { + $driver=strtolower(substr($this->connectionString,0,$pos)); + if($driver==='mssql' || $driver==='dblib') + $pdoClass='CMssqlPdoAdapter'; + } + return new $pdoClass($this->connectionString, $this->username, + $this->password, array(PDO::ATTR_PERSISTENT=>true)); + } + + /** + * Initializes the open db connection. + * This method is invoked right after the db connection is established. + * The default implementation is to set the charset for MySQL and PostgreSQL database connections. + * @param PDO the PDO instance + */ + protected function initConnection($pdo) + { + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + if($this->emulatePrepare && constant('PDO::ATTR_EMULATE_PREPARES')) + $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,true); + + if($this->charset!==null) + { + if(strcasecmp($pdo->getAttribute(PDO::ATTR_DRIVER_NAME),'sqlite')) + $pdo->exec('SET NAMES '.$pdo->quote($this->charset)); + } + } + + /** + * @return PDO the PDO instance, null if the connection is not established yet + */ + public function getPdoInstance() + { + return $this->_pdo; + } + + /** + * Creates a command for execution. + * @param string SQL statement associated with the new command. + * @return CDbCommand the DB command + * @throws CException if the connection is not active + */ + public function createCommand($sql) + { + if($this->getActive()) + return new CDbCommand($this,$sql); + else + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + } + + /** + * @return CDbTransaction the currently active transaction. Null if no active transaction. + */ + public function getCurrentTransaction() + { + if($this->_transaction!==null) + { + if($this->_transaction->getActive()) + return $this->_transaction; + } + return null; + } + + /** + * Starts a transaction. + * @return CDbTransaction the transaction initiated + * @throws CException if the connection is not active + */ + public function beginTransaction() + { + if($this->getActive()) + { + $this->_pdo->beginTransaction(); + return $this->_transaction=new CDbTransaction($this); + } + else + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + } + + /** + * @return CDbSchema the database schema for the current connection + * @throws CException if the connection is not active yet + */ + public function getSchema() + { + if($this->_schema!==null) + return $this->_schema; + else + { + if(!$this->getActive()) + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + $driver=$this->getDriverName(); + switch(strtolower($driver)) + { + case 'pgsql': // PostgreSQL + return $this->_schema=new CPgsqlSchema($this); + case 'mysqli': // MySQL + case 'mysql': + return $this->_schema=new CMysqlSchema($this); + case 'sqlite': // sqlite 3 + case 'sqlite2': // sqlite 2 + return $this->_schema=new CSqliteSchema($this); + case 'mssql': // Mssql driver on windows hosts + case 'dblib': // dblib drivers on linux (and maybe others os) hosts + return $this->_schema=new CMssqlSchema($this); + case 'oci': // Oracle driver + return $this->_schema=new COciSchema($this); + case 'ibm': + default: + throw new CDbException(Yii::t('yii','CDbConnection does not support reading schema for {driver} database.', + array('{driver}'=>$driver))); + } + } + } + + /** + * Returns the SQL command builder for the current DB connection. + * @return CDbCommandBuilder the command builder + * @since 1.0.4 + */ + public function getCommandBuilder() + { + return $this->getSchema()->getCommandBuilder(); + } + + /** + * Returns the ID of the last inserted row or sequence value. + * @param string name of the sequence object (required by some DBMS) + * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object + * @see http://www.php.net/manual/en/function.PDO-lastInsertId.php + */ + public function getLastInsertID($sequenceName='') + { + if($this->getActive()) + return $this->_pdo->lastInsertId($sequenceName); + else + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + } + + /** + * Quotes a string value for use in a query. + * @param string string to be quoted + * @return string the properly quoted string + * @see http://www.php.net/manual/en/function.PDO-quote.php + */ + public function quoteValue($str) + { + if($this->getActive()) + return $this->_pdo->quote($str); + else + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + } + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return $this->getSchema()->quoteTableName($name); + } + + /** + * Quotes a column name for use in a query. + * @param string column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return $this->getSchema()->quoteColumnName($name); + } + + /** + * Determines the PDO type for the specified PHP type. + * @param string The PHP type (obtained by gettype() call). + * @return integer the corresponding PDO type + */ + public function getPdoType($type) + { + static $map=array + ( + 'boolean'=>PDO::PARAM_BOOL, + 'integer'=>PDO::PARAM_INT, + 'string'=>PDO::PARAM_STR, + 'NULL'=>PDO::PARAM_NULL, + ); + return isset($map[$type]) ? $map[$type] : PDO::PARAM_STR; + } + + /** + * @return mixed the case of the column names + * @see http://www.php.net/manual/en/pdo.setattribute.php + */ + public function getColumnCase() + { + return $this->getAttribute(PDO::ATTR_CASE); + } + + /** + * @param mixed the case of the column names + * @see http://www.php.net/manual/en/pdo.setattribute.php + */ + public function setColumnCase($value) + { + $this->setAttribute(PDO::ATTR_CASE,$value); + } + + /** + * @return mixed how the null and empty strings are converted + * @see http://www.php.net/manual/en/pdo.setattribute.php + */ + public function getNullConversion() + { + return $this->getAttribute(PDO::ATTR_ORACLE_NULLS); + } + + /** + * @param mixed how the null and empty strings are converted + * @see http://www.php.net/manual/en/pdo.setattribute.php + */ + public function setNullConversion($value) + { + $this->setAttribute(PDO::ATTR_ORACLE_NULLS,$value); + } + + /** + * @return boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getAutoCommit() + { + return $this->getAttribute(PDO::ATTR_AUTOCOMMIT); + } + + /** + * @param boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setAutoCommit($value) + { + $this->setAttribute(PDO::ATTR_AUTOCOMMIT,$value); + } + + /** + * @return boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getPersistent() + { + return $this->getAttribute(PDO::ATTR_PERSISTENT); + } + + /** + * @param boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setPersistent($value) + { + return $this->setAttribute(PDO::ATTR_PERSISTENT,$value); + } + + /** + * @return string name of the DB driver + */ + public function getDriverName() + { + return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + } + + /** + * @return string the version information of the DB driver + */ + public function getClientVersion() + { + return $this->getAttribute(PDO::ATTR_CLIENT_VERSION); + } + + /** + * @return string the status of the connection + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getConnectionStatus() + { + return $this->getAttribute(PDO::ATTR_CONNECTION_STATUS); + } + + /** + * @return boolean whether the connection performs data prefetching + */ + public function getPrefetch() + { + return $this->getAttribute(PDO::ATTR_PREFETCH); + } + + /** + * @return string the information of DBMS server + */ + public function getServerInfo() + { + return $this->getAttribute(PDO::ATTR_SERVER_INFO); + } + + /** + * @return string the version information of DBMS server + */ + public function getServerVersion() + { + return $this->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * @return int timeout settings for the connection + */ + public function getTimeout() + { + return $this->getAttribute(PDO::ATTR_TIMEOUT); + } + + /** + * Obtains a specific DB connection attribute information. + * @param int the attribute to be queried + * @return mixed the corresponding attribute information + * @see http://www.php.net/manual/en/function.PDO-getAttribute.php + */ + public function getAttribute($name) + { + if($this->getActive()) + return $this->_pdo->getAttribute($name); + else + throw new CDbException(Yii::t('yii','CDbConnection is inactive and cannot perform any DB operations.')); + } + + /** + * Sets an attribute on the database connection. + * @param int the attribute to be set + * @param mixed the attribute value + * @see http://www.php.net/manual/en/function.PDO-setAttribute.php + */ + public function setAttribute($name,$value) + { + if($this->_pdo instanceof PDO) + $this->_pdo->setAttribute($name,$value); + else + $this->_attributes[$name]=$value; + } + + /** + * Returns the statistical results of SQL executions. + * The results returned include the number of SQL statements executed and + * the total time spent. + * In order to use this method, {@link enableProfiling} has to be set true. + * @return array the first element indicates the number of SQL statements executed, + * and the second element the total time spent in SQL execution. + * @since 1.0.6 + */ + public function getStats() + { + $logger=Yii::getLogger(); + $timings=$logger->getProfilingResults(null,'system.db.CDbCommand.query'); + $count=count($timings); + $time=array_sum($timings); + $timings=$logger->getProfilingResults(null,'system.db.CDbCommand.execute'); + $count+=count($timings); + $time+=array_sum($timings); + return array($count,$time); + } +} diff --git a/web/framework-1.0.8/db/CDbDataReader.php b/web/framework-1.0.8/db/CDbDataReader.php new file mode 100644 index 0000000..7cd0f9d --- /dev/null +++ b/web/framework-1.0.8/db/CDbDataReader.php @@ -0,0 +1,221 @@ +<?php +/** + * CDbDataReader class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbDataReader represents a forward-only stream of rows from a query result set. + * + * To read the current row of data, call {@link read}. The method {@link readAll} + * returns all the rows in a single array. + * + * One can also retrieve the rows of data in CDbDataReader by using foreach: + * <pre> + * foreach($reader as $row) + * // $row represents a row of data + * </pre> + * Since CDbDataReader is a forward-only stream, you can only traverse it once. + * + * It is possible to use a specific mode of data fetching by setting + * {@link setFetchMode FetchMode}. See {@link http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php} + * for more details. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbDataReader.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db + * @since 1.0 + */ +class CDbDataReader extends CComponent implements Iterator +{ + private $_statement; + private $_closed=false; + private $_row; + private $_index=-1; + + /** + * Constructor. + * @param CDbCommand the command generating the query result + */ + public function __construct(CDbCommand $command) + { + $this->_statement=$command->getPdoStatement(); + $this->_statement->setFetchMode(PDO::FETCH_ASSOC); + } + + /** + * Binds a column to a PHP variable. + * When rows of data are being fetched, the corresponding column value + * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND. + * @param mixed Number of the column (1-indexed) or name of the column + * in the result set. If using the column name, be aware that the name + * should match the case of the column, as returned by the driver. + * @param mixed Name of the PHP variable to which the column will be bound. + * @param int Data type of the parameter + * @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php + */ + public function bindColumn($column, &$value, $dataType=null) + { + if($dataType===null) + $this->_statement->bindColumn($column,$value); + else + $this->_statement->bindColumn($column,$value,$dataType); + } + + /** + * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php + */ + public function setFetchMode($mode) + { + $params=func_get_args(); + call_user_func_array(array($this->_statement,'setFetchMode'),$params); + } + + /** + * Advances the reader to the next row in a result set. + * @return array|false the current row, false if no more row available + */ + public function read() + { + return $this->_statement->fetch(); + } + + /** + * Returns a single column from the next row of a result set. + * @param int zero-based column index + * @return mixed|false the column of the current row, false if no more row available + */ + public function readColumn($columnIndex) + { + return $this->_statement->fetchColumn($columnIndex); + } + + /** + * Returns an object populated with the next row of data. + * @param string class name of the object to be created and populated + * @param array Elements of this array are passed to the constructor + * @return mixed|false the populated object, false if no more row of data available + */ + public function readObject($className,$fields) + { + return $this->_statement->fetchObject($className,$fields); + } + + /** + * Reads the whole result set into an array. + * @return array the result set (each array element represents a row of data). + * An empty array will be returned if the result contains no row. + */ + public function readAll() + { + return $this->_statement->fetchAll(); + } + + /** + * Advances the reader to the next result when reading the results of a batch of statements. + * This method is only useful when there are multiple result sets + * returned by the query. Not all DBMS support this feature. + */ + public function nextResult() + { + return $this->_statement->nextRowset(); + } + + /** + * Closes the reader. + * This frees up the resources allocated for executing this SQL statement. + * Read attemps after this method call are unpredictable. + */ + public function close() + { + $this->_statement->closeCursor(); + $this->_closed=true; + } + + /** + * @return boolean whether the reader is closed or not. + */ + public function getIsClosed() + { + return $this->_closed; + } + + /** + * @return int number of rows contained in the result. + * Note, most DBMS may not give a meaningful count. + * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows. + */ + public function getRowCount() + { + return $this->_statement->rowCount(); + } + + /** + * @return int the number of columns in the result set. + * Note, even there's no row in the reader, this still gives correct column number. + */ + public function getColumnCount() + { + return $this->_statement->columnCount(); + } + + /** + * Resets the iterator to the initial state. + * This method is required by the interface Iterator. + * @throws CException if this method is invoked twice + */ + public function rewind() + { + if($this->_index<0) + { + $this->_row=$this->_statement->fetch(); + $this->_index=0; + } + else + throw new CDbException(Yii::t('yii','CDbDataReader cannot rewind. It is a forward-only reader.')); + } + + /** + * Returns the index of the current row. + * This method is required by the interface Iterator. + * @return integer the index of the current row. + */ + public function key() + { + return $this->_index; + } + + /** + * Returns the current row. + * This method is required by the interface Iterator. + * @return mixed the current row. + */ + public function current() + { + return $this->_row; + } + + /** + * Moves the internal pointer to the next row. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_row=$this->_statement->fetch(); + $this->_index++; + } + + /** + * Returns whether there is a row of data at current position. + * This method is required by the interface Iterator. + * @return boolean whether there is a row of data at current position. + */ + public function valid() + { + return $this->_row!==false; + } +} diff --git a/web/framework-1.0.8/db/CDbException.php b/web/framework-1.0.8/db/CDbException.php new file mode 100644 index 0000000..18f14e4 --- /dev/null +++ b/web/framework-1.0.8/db/CDbException.php @@ -0,0 +1,21 @@ +<?php +/** + * CDbException class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbException represents an exception that is caused by some DB-related operations. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbException.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db + * @since 1.0 + */ +class CDbException extends CException +{ +} \ No newline at end of file diff --git a/web/framework-1.0.8/db/CDbTransaction.php b/web/framework-1.0.8/db/CDbTransaction.php new file mode 100644 index 0000000..006f134 --- /dev/null +++ b/web/framework-1.0.8/db/CDbTransaction.php @@ -0,0 +1,108 @@ +<?php +/** + * CDbTransaction class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbTransaction represents a DB transaction. + * + * It is usually created by calling {@link CDbConnection::beginTransaction}. + * + * The following code is a common scenario of using transactions: + * <pre> + * $transaction=$connection->beginTransaction(); + * try + * { + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbTransaction.php 1021 2009-05-11 12:33:44Z qiang.xue $ + * @package system.db + * @since 1.0 + */ +class CDbTransaction extends CComponent +{ + private $_connection=null; + private $_active; + + /** + * Constructor. + * @param CDbConnection the connection associated with this transaction + * @see CDbConnection::beginTransaction + */ + public function __construct(CDbConnection $connection) + { + $this->_connection=$connection; + $this->setActive(true); + } + + /** + * Commits a transaction. + * @throws CException if the transaction or the DB connection is not active. + */ + public function commit() + { + if($this->_active && $this->_connection->getActive()) + { + Yii::trace('Committing transaction','system.db.CDbTransaction'); + $this->_connection->getPdoInstance()->commit(); + $this->_active=false; + } + else + throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations.')); + } + + /** + * Rolls back a transaction. + * @throws CException if the transaction or the DB connection is not active. + */ + public function rollback() + { + if($this->_active && $this->_connection->getActive()) + { + Yii::trace('Rolling back transaction','system.db.CDbTransaction'); + $this->_connection->getPdoInstance()->rollBack(); + $this->_active=false; + } + else + throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations.')); + } + + /** + * @return CDbConnection the DB connection for this transaction + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return boolean whether this transaction is active + */ + public function getActive() + { + return $this->_active; + } + + /** + * @param boolean whether this transaction is active + */ + protected function setActive($value) + { + $this->_active=$value; + } +} diff --git a/web/framework-1.0.8/db/ar/CActiveFinder.php b/web/framework-1.0.8/db/ar/CActiveFinder.php new file mode 100644 index 0000000..996da45 --- /dev/null +++ b/web/framework-1.0.8/db/ar/CActiveFinder.php @@ -0,0 +1,1359 @@ +<?php +/** + * CActiveRecord class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CActiveFinder implements eager loading and lazy loading of related active records. + * + * When used in eager loading, this class provides the same set of find methods as + * {@link CActiveRecord}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveFinder.php 1296 2009-08-06 21:44:44Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CActiveFinder extends CComponent +{ + /** + * @var boolean join all tables all at once. Defaults to false. + * This property is internally used. + * @since 1.0.2 + */ + public $joinAll=false; + /** + * @var boolean whether the base model has limit or offset. + * This property is internally used. + * @since 1.0.2 + */ + public $baseLimited; + + private $_joinCount=0; + private $_joinTree; + private $_builder; + private $_criteria; // the criteria generated via named scope + + /** + * Constructor. + * A join tree is built up based on the declared relationships between active record classes. + * @param CActiveRecord the model that initiates the active finding process + * @param mixed the relation names to be actively looked for + * @param CDbCriteria the criteria associated with the named scopes (since version 1.0.5) + */ + public function __construct($model,$with,$criteria=null) + { + $this->_criteria=$criteria; + $this->_builder=$model->getCommandBuilder(); + $this->_joinTree=new CJoinElement($this,$model); + $this->buildJoinTree($this->_joinTree,$with); + } + + /** + * Uses the most aggressive join approach. + * By default, several join statements may be generated in order to avoid + * fetching duplicated data. By calling this method, all tables will be joined + * together all at once. + * @param boolean whether we should enforce join even when a limit option is placed on the primary table query. + * Defaults to true. If false, we would still use two queries when there is a HAS_MANY/MANY_MANY relation and + * the primary table has a LIMIT option. This parameter is available since version 1.0.3. + * @return CActiveFinder the finder object + * @since 1.0.2 + */ + public function together($ignoreLimit=true) + { + $this->joinAll=true; + if($ignoreLimit) + $this->baseLimited=false; + return $this; + } + + private function query($criteria,$all=false) + { + if($this->_criteria!==null) + { + $this->_criteria->mergeWith($criteria); + $criteria=$this->_criteria; + } + + $this->_joinTree->find($criteria); + $this->_joinTree->afterFind(); + + if($all) + return array_values($this->_joinTree->records); + else if(count($this->_joinTree->records)) + return reset($this->_joinTree->records); + else + return null; + } + + /** + * This is the relational version of {@link CActiveRecord::find()}. + */ + public function find($condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.find() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createCriteria($condition,$params); + return $this->query($criteria); + } + + /** + * This is the relational version of {@link CActiveRecord::findAll()}. + */ + public function findAll($condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findAll() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createCriteria($condition,$params); + return $this->query($criteria,true); + } + + /** + * This is the relational version of {@link CActiveRecord::findByPk()}. + */ + public function findByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findByPk() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createPkCriteria($this->_joinTree->model->getTableSchema(),$pk,$condition,$params); + return $this->query($criteria); + } + + /** + * This is the relational version of {@link CActiveRecord::findAllByPk()}. + */ + public function findAllByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findAllByPk() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createPkCriteria($this->_joinTree->model->getTableSchema(),$pk,$condition,$params); + return $this->query($criteria,true); + } + + /** + * This is the relational version of {@link CActiveRecord::findByAttributes()}. + */ + public function findByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findByAttributes() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createColumnCriteria($this->_joinTree->model->getTableSchema(),$attributes,$condition,$params); + return $this->query($criteria); + } + + /** + * This is the relational version of {@link CActiveRecord::findAllByAttributes()}. + */ + public function findAllByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findAllByAttributes() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createColumnCriteria($this->_joinTree->model->getTableSchema(),$attributes,$condition,$params); + return $this->query($criteria,true); + } + + /** + * This is the relational version of {@link CActiveRecord::findBySql()}. + */ + public function findBySql($sql,$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findBySql() eagerly','system.db.ar.CActiveRecord'); + if(($row=$this->_builder->createSqlCommand($sql,$params)->queryRow())!==false) + { + $baseRecord=$this->_joinTree->model->populateRecord($row,false); + $this->_joinTree->findWithBase($baseRecord); + $this->_joinTree->afterFind(); + return $baseRecord; + } + } + + /** + * This is the relational version of {@link CActiveRecord::findAllBySql()}. + */ + public function findAllBySql($sql,$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.findAllBySql() eagerly','system.db.ar.CActiveRecord'); + if(($rows=$this->_builder->createSqlCommand($sql,$params)->queryAll())!==array()) + { + $baseRecords=$this->_joinTree->model->populateRecords($rows,false); + $this->_joinTree->findWithBase($baseRecords); + $this->_joinTree->afterFind(); + return $baseRecords; + } + else + return array(); + } + + /** + * This is the relational version of {@link CActiveRecord::count()}. + * @since 1.0.3 + */ + public function count($condition='',$params=array()) + { + Yii::trace(get_class($this->_joinTree->model).'.count() eagerly','system.db.ar.CActiveRecord'); + $criteria=$this->_builder->createCriteria($condition,$params); + if($this->_criteria!==null) + { + $this->_criteria->mergeWith($criteria); + $criteria=$this->_criteria; + } + return $this->_joinTree->count($criteria); + } + + /** + * Finds the related objects for the specified active record. + * This method is internally invoked by {@link CActiveRecord} to support lazy loading. + * @param CActiveRecord the base record whose related objects are to be loaded + */ + public function lazyFind($baseRecord) + { + $this->_joinTree->lazyFind($baseRecord); + if(!empty($this->_joinTree->children)) + { + $child=reset($this->_joinTree->children); + $child->afterFind(); + } + } + + /** + * Builds up the join tree representing the relationships involved in this query. + * @param CJoinElement the parent tree node + * @param mixed the names of the related objects relative to the parent tree node + * @param array additional query options to be merged with the relation + */ + private function buildJoinTree($parent,$with,$options=null) + { + if($parent instanceof CStatElement) + throw new CDbException(Yii::t('yii','The STAT relation "{name}" cannot have child relations.', + array('{name}'=>$parent->relation->name))); + + if(is_string($with)) + { + if(($pos=strrpos($with,'.'))!==false) + { + $parent=$this->buildJoinTree($parent,substr($with,0,$pos)); + $with=substr($with,$pos+1); + } + + // named scope + if(($pos=strpos($with,':'))!==false) + { + $scopes=explode(':',substr($with,$pos+1)); + $with=substr($with,0,$pos); + } + + if(isset($parent->children[$with])) + return $parent->children[$with]; + + if(($relation=$parent->model->getActiveRelation($with))===null) + throw new CDbException(Yii::t('yii','Relation "{name}" is not defined in active record class "{class}".', + array('{class}'=>get_class($parent->model), '{name}'=>$with))); + + $relation=clone $relation; + $model=CActiveRecord::model($relation->className); + if(($scope=$model->defaultScope())!==array()) + $relation->mergeWith($scope); + if(isset($scopes) && !empty($scopes)) + { + $scs=$model->scopes(); + foreach($scopes as $scope) + { + if(isset($scs[$scope])) + $relation->mergeWith($scs[$scope]); + else + throw new CDbException(Yii::t('yii','Active record class "{class}" does not have a scope named "{scope}".', + array('{class}'=>get_class($model), '{scope}'=>$scope))); + } + } + + // dynamic options + if($options!==null) + $relation->mergeWith($options); + + if($relation instanceof CStatRelation) + return new CStatElement($this,$relation,$parent); + else + { + $element=$parent->children[$with]=new CJoinElement($this,$relation,$parent,++$this->_joinCount); + if(!empty($relation->with)) + $this->buildJoinTree($element,$relation->with); + return $element; + } + } + + // $with is an array, keys are relation name, values are relation spec + foreach($with as $key=>$value) + { + if(is_string($value)) // the value is a relation name + $this->buildJoinTree($parent,$value); + else if(is_string($key) && is_array($value)) + $element=$this->buildJoinTree($parent,$key,$value); + } + } +} + + +/** + * CJoinElement represents a tree node in the join tree created by {@link CActiveFinder}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveFinder.php 1296 2009-08-06 21:44:44Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CJoinElement +{ + /** + * @var integer the unique ID of this tree node + */ + public $id; + /** + * @var CActiveRelation the relation represented by this tree node + */ + public $relation; + /** + * @var CActiveRecord the model associated with this tree node + */ + public $model; + /** + * @var array list of active records found by the queries. They are indexed by primary key values. + */ + public $records=array(); + /** + * @var array list of child join elements + */ + public $children=array(); + /** + * @var array list of stat elements + * @since 1.0.4 + */ + public $stats=array(); + /** + * @var string table alias for this join element + */ + public $tableAlias; + + private $_finder; + private $_builder; + private $_parent; + private $_pkAlias; // string or name=>alias + private $_columnAliases=array(); // name=>alias + private $_joined=false; + private $_table; + private $_related=array(); // PK, relation name, related PK => true + + /** + * Constructor. + * @param CActiveFinder the finder + * @param mixed the relation (if the second parameter is not null) + * or the model (if the second parameter is null) associated with this tree node. + * @param CJoinElement the parent tree node + * @param integer the ID of this tree node that is unique among all the tree nodes + */ + public function __construct($finder,$relation,$parent=null,$id=0) + { + $this->_finder=$finder; + $this->id=$id; + if($parent!==null) + { + $this->relation=$relation; + $this->_parent=$parent; + $this->_builder=$parent->_builder; + $this->tableAlias=$relation->alias===null?'t'.$id:$relation->alias; + $this->model=CActiveRecord::model($relation->className); + $this->_table=$this->model->getTableSchema(); + } + else // root element, the first parameter is the model. + { + $this->model=$relation; + $this->_builder=$relation->getCommandBuilder(); + $this->_table=$relation->getTableSchema(); + } + + // set up column aliases, such as t1_c2 + $table=$this->_table; + if($this->model->getDbConnection()->getDriverName()==='oci') // Issue 482 + $prefix='T'.$id.'_C'; + else + $prefix='t'.$id.'_c'; + foreach($table->getColumnNames() as $key=>$name) + { + $alias=$prefix.$key; + $this->_columnAliases[$name]=$alias; + if($table->primaryKey===$name) + $this->_pkAlias=$alias; + else if(is_array($table->primaryKey) && in_array($name,$table->primaryKey)) + $this->_pkAlias[$name]=$alias; + } + } + + /** + * Performs the recursive finding with the criteria. + * @param CDbCriteria the query criteria + */ + public function find($criteria=null) + { + if($this->_parent===null) // root element + { + $query=new CJoinQuery($this,$criteria); + if($this->_finder->baseLimited===null) + $this->_finder->baseLimited=($criteria->offset>=0 || $criteria->limit>=0); + $this->buildQuery($query); + $this->runQuery($query); + } + else if(!$this->_joined && !empty($this->_parent->records)) // not joined before + { + $query=new CJoinQuery($this->_parent); + $this->_joined=true; + $query->join($this); + $this->buildQuery($query); + $this->_parent->runQuery($query); + } + + foreach($this->children as $child) // find recursively + $child->find(); + + foreach($this->stats as $stat) + $stat->query(); + } + + /** + * Performs lazy find with the specified base record. + * @param CActiveRecord the active record whose related object is to be fetched. + */ + public function lazyFind($baseRecord) + { + if(is_string($this->_table->primaryKey)) + $this->records[$baseRecord->{$this->_table->primaryKey}]=$baseRecord; + else + { + $pk=array(); + foreach($this->_table->primaryKey as $name) + $pk[$name]=$baseRecord->$name; + $this->records[serialize($pk)]=$baseRecord; + } + + foreach($this->stats as $stat) + $stat->query(); + + if(empty($this->children)) + return; + + $child=reset($this->children); + $query=new CJoinQuery($this); + $this->_joined=true; + $child->_joined=true; + $query->join($child); + if($child->relation instanceof CHasManyRelation) + { + $query->limit=$child->relation->limit; + $query->offset=$child->relation->offset; + if($this->_finder->baseLimited===null) + $this->_finder->baseLimited=($query->offset>=0 || $query->limit>=0); + $query->groups[]=str_replace($child->relation->aliasToken.'.',$child->tableAlias.'.',$child->relation->group); + $query->havings[]=str_replace($child->relation->aliasToken.'.',$child->tableAlias.'.',$child->relation->having); + } + $child->buildQuery($query); + $this->runQuery($query); + foreach($child->children as $c) + $c->find(); + } + + /** + * Performs the eager loading with the base records ready. + * @param mixed the available base record(s). + */ + public function findWithBase($baseRecords) + { + if(!is_array($baseRecords)) + $baseRecords=array($baseRecords); + if(is_string($this->_table->primaryKey)) + { + foreach($baseRecords as $baseRecord) + $this->records[$baseRecord->{$this->_table->primaryKey}]=$baseRecord; + } + else + { + foreach($baseRecords as $baseRecord) + { + $pk=array(); + foreach($this->_table->primaryKey as $name) + $pk[$name]=$baseRecord->$name; + $this->records[serialize($pk)]=$baseRecord; + } + } + + $query=new CJoinQuery($this); + $this->buildQuery($query); + if(count($query->joins)>1) + $this->runQuery($query); + foreach($this->children as $child) + $child->find(); + + foreach($this->stats as $stat) + $stat->query(); + } + + /** + * Count the number of primary records returned by the join statement. + * @param CDbCriteria the query criteria + * @return integer number of primary records. + * @since 1.0.3 + */ + public function count($criteria=null) + { + $query=new CJoinQuery($this,$criteria); + // ensure only one big join statement is used + $this->_finder->baseLimited=false; + $this->_finder->joinAll=true; + $this->buildQuery($query); + + $select=is_array($criteria->select) ? implode(',',$criteria->select) : $criteria->select; + if($select!=='*' && !strncasecmp($select,'count',5)) + $query->selects=array($select); + else if(is_string($this->_table->primaryKey)) + { + $prefix=$this->getColumnPrefix(); + $schema=$this->_builder->getSchema(); + $column=$prefix.$schema->quoteColumnName($this->_table->primaryKey); + $query->selects=array("COUNT(DISTINCT $column)"); + } + else + $query->selects=array("COUNT(*)"); + + $query->orders=$query->groups=$query->havings=array(); + $command=$query->createCommand($this->_builder); + return $command->queryScalar(); + } + + /** + * Calls {@link CActiveRecord::afterFind} of all the records. + * @since 1.0.3 + */ + public function afterFind() + { + foreach($this->records as $record) + $record->afterFindInternal(); + foreach($this->children as $child) + $child->afterFind(); + } + + /** + * Builds the join query with all descendant HAS_ONE and BELONGS_TO nodes. + * @param CJoinQuery the query being built up + */ + public function buildQuery($query) + { + foreach($this->children as $child) + { + if($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation + || $child->relation->together || ($this->_finder->joinAll && !$this->_finder->baseLimited)) + { + $child->_joined=true; + $query->join($child); + $child->buildQuery($query); + } + } + } + + /** + * Executes the join query and populates the query results. + * @param CJoinQuery the query to be executed. + */ + public function runQuery($query) + { + $command=$query->createCommand($this->_builder); + foreach($command->queryAll() as $row) + $this->populateRecord($query,$row); + } + + /** + * Populates the active records with the query data. + * @param CJoinQuery the query executed + * @param array a row of data + * @return CActiveRecord the populated record + */ + private function populateRecord($query,$row) + { + // determine the primary key value + if(is_string($this->_pkAlias)) // single key + { + if(isset($row[$this->_pkAlias])) + $pk=$row[$this->_pkAlias]; + else // no matching related objects + return null; + } + else // is_array, composite key + { + $pk=array(); + foreach($this->_pkAlias as $name=>$alias) + { + if(isset($row[$alias])) + $pk[$name]=$row[$alias]; + else // no matching related objects + return null; + } + $pk=serialize($pk); + } + + // retrieve or populate the record according to the primary key value + if(isset($this->records[$pk])) + $record=$this->records[$pk]; + else + { + $attributes=array(); + $aliases=array_flip($this->_columnAliases); + foreach($row as $alias=>$value) + { + if(isset($aliases[$alias])) + $attributes[$aliases[$alias]]=$value; + } + $record=$this->model->populateRecord($attributes,false); + foreach($this->children as $child) + $record->addRelatedRecord($child->relation->name,null,$child->relation instanceof CHasManyRelation); + $this->records[$pk]=$record; + } + + // populate child records recursively + foreach($this->children as $child) + { + if(!isset($query->elements[$child->id])) + continue; + $childRecord=$child->populateRecord($query,$row); + if($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation) + $record->addRelatedRecord($child->relation->name,$childRecord,false); + else // has_many and many_many + { + // need to double check to avoid adding duplicated related objects + if($childRecord instanceof CActiveRecord) + $fpk=serialize($childRecord->getPrimaryKey()); + else + $fpk=0; + if(!isset($this->_related[$pk][$child->relation->name][$fpk])) + { + if($childRecord instanceof CActiveRecord && $child->relation->index!==null) + $index=$childRecord->{$child->relation->index}; + else + $index=true; + $record->addRelatedRecord($child->relation->name,$childRecord,$index); + $this->_related[$pk][$child->relation->name][$fpk]=true; + } + } + } + + return $record; + } + + /** + * @return string the table name and the table alias (if any). This can be used directly in SQL query without escaping. + */ + public function getTableNameWithAlias() + { + if($this->tableAlias!==null) + return $this->_table->rawName . ' ' . $this->tableAlias; + else + return $this->_table->rawName; + } + + /** + * Generates the list of columns to be selected. + * Columns will be properly aliased and primary keys will be added to selection if they are not specified. + * @param mixed columns to be selected. Defaults to '*', indicating all columns. + * @return string the column selection + */ + public function getColumnSelect($select='*') + { + $schema=$this->_builder->getSchema(); + $prefix=$this->getColumnPrefix(); + $columns=array(); + if($select==='*') + { + foreach($this->_table->getColumnNames() as $name) + $columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($this->_columnAliases[$name]); + } + else + { + if(is_string($select)) + $select=explode(',',$select); + $selected=array(); + foreach($select as $name) + { + $name=trim($name); + $matches=array(); + if(($pos=strrpos($name,'.'))!==false) + $key=substr($name,$pos+1); + else + $key=$name; + if(isset($this->_columnAliases[$key])) // simple column names + { + $columns[]=$prefix.$schema->quoteColumnName($key).' AS '.$schema->quoteColumnName($this->_columnAliases[$key]); + $selected[$this->_columnAliases[$key]]=1; + } + else if(preg_match('/^(.*?)\s+AS\s+(\w+)$/i',$name,$matches)) // if the column is already aliased + { + $alias=$matches[2]; + if(!isset($this->_columnAliases[$alias]) || $this->_columnAliases[$alias]!==$alias) + { + $this->_columnAliases[$alias]=$alias; + $columns[]=$name; + $selected[$alias]=1; + } + } + else + throw new CDbException(Yii::t('yii','Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.', + array('{class}'=>get_class($this->model), '{column}'=>$name))); + } + // add primary key selection if they are not selected + if(is_string($this->_pkAlias) && !isset($selected[$this->_pkAlias])) + $columns[]=$prefix.$schema->quoteColumnName($this->_table->primaryKey).' AS '.$schema->quoteColumnName($this->_pkAlias); + else if(is_array($this->_pkAlias)) + { + foreach($this->_table->primaryKey as $name) + if(!isset($selected[$name])) + $columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($this->_pkAlias[$name]); + } + } + + $select=implode(', ',$columns); + if($this->relation!==null) + return str_replace($this->relation->aliasToken.'.', $prefix, $select); + else + return $select; + } + + /** + * @return string the primary key selection + */ + public function getPrimaryKeySelect() + { + $schema=$this->_builder->getSchema(); + $prefix=$this->getColumnPrefix(); + $columns=array(); + if(is_string($this->_pkAlias)) + $columns[]=$prefix.$schema->quoteColumnName($this->_table->primaryKey).' AS '.$schema->quoteColumnName($this->_pkAlias); + else if(is_array($this->_pkAlias)) + { + foreach($this->_pkAlias as $name=>$alias) + $columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($alias); + } + return implode(', ',$columns); + } + + /** + * @return string the condition that specifies only the rows with the selected primary key values. + */ + public function getPrimaryKeyRange() + { + if(empty($this->records)) + return ''; + $values=array_keys($this->records); + if(is_array($this->_table->primaryKey)) + { + foreach($values as &$value) + $value=unserialize($value); + } + return $this->_builder->createInCondition($this->_table,$this->_table->primaryKey,$values,$this->getColumnPrefix()); + } + + /** + * @return string the WHERE clause. Column references are properly disambiguated. + */ + public function getCondition() + { + if($this->relation->condition!=='' && $this->tableAlias!==null) + return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->condition); + else + return $this->relation->condition; + } + + /** + * @return string the ORDER BY clause. Column references are properly disambiguated. + */ + public function getOrder() + { + if($this->relation->order!=='' && $this->tableAlias!==null) + return str_replace($this->relation->aliasToken.'.',$this->tableAlias.'.',$this->relation->order); + else + return $this->relation->order; + } + + /** + * @return string the GROUP BY clause. Column references are properly disambiguated. + * @since 1.0.4 + */ + public function getGroupBy() + { + if($this->relation->group!=='' && $this->tableAlias!==null) + return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->group); + else + return $this->relation->group; + } + + /** + * @return string the HAVING clause. Column references are properly disambiguated. + * @since 1.0.4 + */ + public function getHaving() + { + if($this->relation->having!=='' && $this->tableAlias!==null) + return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->having); + else + return $this->relation->having; + } + + /** + * @return string the column prefix for column reference disambiguation + */ + public function getColumnPrefix() + { + if($this->tableAlias!==null) + return $this->tableAlias.'.'; + else + return $this->_table->rawName.'.'; + } + + /** + * @return string the join statement (this node joins with its parent) + */ + public function getJoinCondition() + { + $parent=$this->_parent; + $relation=$this->relation; + if($this->relation instanceof CManyManyRelation) + { + if(!preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->relation->foreignKey,$matches)) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".', + array('{class}'=>get_class($parent->model),'{relation}'=>$this->relation->name))); + + $schema=$this->_builder->getSchema(); + if(($joinTable=$schema->getTable($matches[1]))===null) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.', + array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{joinTable}'=>$matches[1]))); + $fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY); + + return $this->joinManyMany($joinTable,$fks,$parent); + } + else + { + $fks=preg_split('/[\s,]+/',$relation->foreignKey,-1,PREG_SPLIT_NO_EMPTY); + if($this->relation instanceof CBelongsToRelation) + { + $pke=$this; + $fke=$parent; + } + else + { + $pke=$parent; + $fke=$this; + } + return $this->joinOneMany($fke,$fks,$pke,$parent); + } + } + + /** + * Generates the join statement for one-many relationship. + * This works for HAS_ONE, HAS_MANY and BELONGS_TO. + * @param CJoinElement the join element containing foreign keys + * @param array the foreign keys + * @param CJoinElement the join element containg primary keys + * @param CJoinElement the parent join element + * @return string the join statement + * @throws CDbException if a foreign key is invalid + */ + private function joinOneMany($fke,$fks,$pke,$parent) + { + $schema=$this->_builder->getSchema(); + $joins=array(); + foreach($fks as $i=>$fk) + { + if(!isset($fke->_table->columns[$fk])) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".', + array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{key}'=>$fk, '{table}'=>$fke->_table->name))); + + if(isset($fke->_table->foreignKeys[$fk])) + $pk=$fke->_table->foreignKeys[$fk][1]; + else // FK constraints undefined + { + if(is_array($pke->_table->primaryKey)) // composite PK + $pk=$pke->_table->primaryKey[$i]; + else + $pk=$pke->_table->primaryKey; + } + $joins[]=$fke->getColumnPrefix().$schema->quoteColumnName($fk) . '=' . $pke->getColumnPrefix().$schema->quoteColumnName($pk); + } + if(!empty($this->relation->on)) + $joins[]=str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->on); + return $this->relation->joinType . ' ' . $this->getTableNameWithAlias() . ' ON (' . implode(') AND (',$joins).')'; + } + + /** + * Generates the join statement for many-many relationship. + * @param CDbTableSchema the join table + * @param array the foreign keys + * @param CJoinElement the parent join element + * @return string the join statement + * @throws CDbException if a foreign key is invalid + */ + private function joinManyMany($joinTable,$fks,$parent) + { + $schema=$this->_builder->getSchema(); + $joinAlias=$this->relation->name.'_'.$this->tableAlias; + $parentCondition=array(); + $childCondition=array(); + foreach($fks as $i=>$fk) + { + if(!isset($joinTable->columns[$fk])) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".', + array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{key}'=>$fk, '{table}'=>$joinTable->name))); + + if(isset($joinTable->foreignKeys[$fk])) + { + list($tableName,$pk)=$joinTable->foreignKeys[$fk]; + if(!isset($parentCondition[$pk]) && $schema->compareTableNames($parent->_table->rawName,$tableName)) + $parentCondition[$pk]=$parent->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk); + else if(!isset($childCondition[$pk]) && $schema->compareTableNames($this->_table->rawName,$tableName)) + $childCondition[$pk]=$this->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk); + else + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.', + array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{key}'=>$fk))); + } + else // FK constraints not defined + { + if($i<count($parent->_table->primaryKey)) + { + $pk=is_array($parent->_table->primaryKey) ? $parent->_table->primaryKey[$i] : $parent->_table->primaryKey; + $parentCondition[$pk]=$parent->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk); + } + else + { + $j=$i-count($parent->_table->primaryKey); + $pk=is_array($this->_table->primaryKey) ? $this->_table->primaryKey[$j] : $this->_table->primaryKey; + $childCondition[$pk]=$this->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk); + } + } + } + if($parentCondition!==array() && $childCondition!==array()) + { + $join=$this->relation->joinType.' '.$joinTable->rawName.' '.$joinAlias; + $join.=' ON ('.implode(') AND (',$parentCondition).')'; + $join.=' '.$this->relation->joinType.' '.$this->getTableNameWithAlias(); + $join.=' ON ('.implode(') AND (',$childCondition).')'; + if(!empty($this->relation->on)) + $join.=' AND ('.str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->on).')'; + return $join; + } + else + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.', + array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name))); + } +} + + +/** + * CJoinQuery represents a JOIN SQL statement. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveFinder.php 1296 2009-08-06 21:44:44Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CJoinQuery +{ + /** + * @var array list of column selections + */ + public $selects=array(); + /** + * @var array list of join statement + */ + public $joins=array(); + /** + * @var array list of WHERE clauses + */ + public $conditions=array(); + /** + * @var array list of ORDER BY clauses + */ + public $orders=array(); + /** + * @var array list of GROUP BY clauses + */ + public $groups=array(); + /** + * @var array list of HAVING clauses + */ + public $havings=array(); + /** + * @var integer row limit + */ + public $limit=-1; + /** + * @var integer row offset + */ + public $offset=-1; + /** + * @var array list of query parameters + */ + public $params=array(); + /** + * @var array list of join element IDs (id=>true) + */ + public $elements=array(); + + /** + * Constructor. + * @param CJoinElement The root join tree. + * @param CDbCriteria the query criteria + */ + public function __construct($joinElement,$criteria=null) + { + if($criteria!==null) + { + $this->selects[]=$joinElement->getColumnSelect($criteria->select); + $this->joins[]=$joinElement->getTableNameWithAlias(); + $this->joins[]=$criteria->join; + $this->conditions[]=$criteria->condition; + $this->orders[]=$criteria->order; + $this->groups[]=$criteria->group; + $this->havings[]=$criteria->having; + $this->limit=$criteria->limit; + $this->offset=$criteria->offset; + $this->params=$criteria->params; + } + else + { + $this->selects[]=$joinElement->getPrimaryKeySelect(); + $this->joins[]=$joinElement->getTableNameWithAlias(); + $this->conditions[]=$joinElement->getPrimaryKeyRange(); + } + $this->elements[$joinElement->id]=true; + } + + /** + * Joins with another join element + * @param CJoinElement the element to be joined + */ + public function join($element) + { + $this->selects[]=$element->getColumnSelect($element->relation->select); + $this->conditions[]=$element->getCondition(); + $this->orders[]=$element->getOrder(); + $this->joins[]=$element->getJoinCondition(); + $this->groups[]=$element->getGroupBy(); + $this->havings[]=$element->getHaving(); + + if(is_array($element->relation->params)) + { + if(is_array($this->params)) + $this->params=array_merge($this->params,$element->relation->params); + else + $this->params=$element->relation->params; + } + $this->elements[$element->id]=true; + } + + /** + * Creates the SQL statement. + * @param CDbCommandBuilder the command builder + * @return string the SQL statement + */ + public function createCommand($builder) + { + $sql='SELECT ' . implode(', ',$this->selects); + $sql.=' FROM ' . implode(' ',$this->joins); + + $conditions=array(); + foreach($this->conditions as $condition) + if($condition!=='') + $conditions[]=$condition; + if($conditions!==array()) + $sql.=' WHERE (' . implode(') AND (',$conditions).')'; + + $groups=array(); + foreach($this->groups as $group) + if($group!=='') + $groups[]=$group; + if($groups!==array()) + $sql.=' GROUP BY ' . implode(', ',$groups); + + $havings=array(); + foreach($this->havings as $having) + if($having!=='') + $havings[]=$having; + if($havings!==array()) + $sql.=' HAVING (' . implode(') AND (',$havings).')'; + + $orders=array(); + foreach($this->orders as $order) + if($order!=='') + $orders[]=$order; + if($orders!==array()) + $sql.=' ORDER BY ' . implode(', ',$orders); + + $sql=$builder->applyLimit($sql,$this->limit,$this->offset); + $command=$builder->getDbConnection()->createCommand($sql); + $builder->bindValues($command,$this->params); + return $command; + } +} + + +/** + * CStatElement represents STAT join element for {@link CActiveFinder}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveFinder.php 1296 2009-08-06 21:44:44Z qiang.xue $ + * @package system.db.ar + * @since 1.0.4 + */ +class CStatElement +{ + /** + * @var CActiveRelation the relation represented by this tree node + */ + public $relation; + + private $_finder; + private $_parent; + + /** + * Constructor. + * @param CActiveFinder the finder + * @param CStatRelation the STAT relation + * @param CJoinElement the join element owning this STAT element + */ + public function __construct($finder,$relation,$parent) + { + $this->_finder=$finder; + $this->_parent=$parent; + $this->relation=$relation; + $parent->stats[]=$this; + } + + /** + * Performs the STAT query. + */ + public function query() + { + if(preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->relation->foreignKey,$matches)) + $this->queryManyMany($matches[1],$matches[2]); + else + $this->queryOneMany(); + } + + private function queryOneMany() + { + $relation=$this->relation; + $model=CActiveRecord::model($relation->className); + $builder=$model->getCommandBuilder(); + $schema=$builder->getSchema(); + $table=$model->getTableSchema(); + $pkTable=$this->_parent->model->getTableSchema(); + + $fks=preg_split('/[\s,]+/',$relation->foreignKey,-1,PREG_SPLIT_NO_EMPTY); + if(count($fks)!==count($pkTable->primaryKey)) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".', + array('{class}'=>get_class($parent->model), '{relation}'=>$relation->name, '{table}'=>$pkTable->name))); + + // set up mapping between fk and pk columns + $map=array(); // pk=>fk + foreach($fks as $i=>$fk) + { + if(!isset($table->columns[$fk])) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".', + array('{class}'=>get_class($parent->model), '{relation}'=>$relation->name, '{key}'=>$fk, '{table}'=>$table->name))); + + if(isset($table->foreignKeys[$fk])) + { + list($tableName,$pk)=$table->foreignKeys[$fk]; + if($schema->compareTableNames($pkTable->rawName,$tableName)) + $map[$pk]=$fk; + else + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".', + array('{class}'=>get_class($parent->model), '{relation}'=>$relation->name, '{key}'=>$fk, '{table}'=>$pkTable->name))); + } + else // FK constraints undefined + { + if(is_array($table->primaryKey)) // composite PK + $map[$table->primaryKey[$i]]=$fk; + } + } + + $records=$this->_parent->records; + + $where=empty($relation->condition)?'' : ' WHERE ('.$relation->condition.')'; + $group=empty($relation->group)?'' : ', '.$relation->group; + $having=empty($relation->having)?'' : ' AND ('.$relation->having.')'; + $order=empty($relation->order)?'' : ' ORDER BY '.$relation->order; + + $c=$schema->quoteColumnName('c'); + $s=$schema->quoteColumnName('s'); + + // generate and perform query + if(count($fks)===1) // single column FK + { + $col=$table->columns[$fks[0]]->rawName; + $sql="SELECT $col AS $c, {$relation->select} AS $s FROM {$table->rawName}" + .$where + ." GROUP BY $col".$group + ." HAVING ".$builder->createInCondition($table,$fks[0],array_keys($records)) + .$having.$order; + $command=$builder->getDbConnection()->createCommand($sql); + if(is_array($relation->params)) + $builder->bindValues($command,$relation->params); + $stats=array(); + foreach($command->queryAll() as $row) + $stats[$row['c']]=$row['s']; + } + else // composite FK + { + $keys=array_keys($records); + foreach($keys as &$key) + { + $key2=unserialize($key); + $key=array(); + foreach($pkTable->primaryKey as $pk) + $key[$map[$pk]]=$key2[$pk]; + } + $cols=array(); + foreach($pkTable->primaryKey as $n=>$pk) + { + $name=$table->columns[$map[$pk]]->rawName; + $cols[$name]=$name.' AS '.$schema->quoteColumnName('c'.$n); + } + $sql='SELECT '.implode(', ',$cols).", {$relation->select} AS $s FROM {$table->rawName}" + .$where + .' GROUP BY '.implode(', ',array_keys($cols)).$group + .' HAVING '.$builder->createInCondition($table,$fks,$keys) + .$having.$order; + $command=$builder->getDbConnection()->createCommand($sql); + if(is_array($relation->params)) + $builder->bindValues($command,$relation->params); + $stats=array(); + foreach($command->queryAll() as $row) + { + $key=array(); + foreach($pkTable->primaryKey as $n=>$pk) + $key[$pk]=$row['c'.$n]; + $stats[serialize($key)]=$row['s']; + } + } + + // populate the results into existing records + foreach($records as $pk=>$record) + $record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$relation->defaultValue,false); + } + + private function queryManyMany($joinTableName,$keys) + { + $relation=$this->relation; + $model=CActiveRecord::model($relation->className); + $table=$model->getTableSchema(); + $builder=$model->getCommandBuilder(); + $schema=$builder->getSchema(); + $pkTable=$this->_parent->model->getTableSchema(); + + if(($joinTable=$builder->getSchema()->getTable($joinTableName))===null) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.', + array('{class}'=>get_class($this->_parent->model), '{relation}'=>$relation->name, '{joinTable}'=>$joinTableName))); + + $fks=preg_split('/[\s,]+/',$keys,-1,PREG_SPLIT_NO_EMPTY); + if(count($fks)!==count($table->primaryKey)+count($pkTable->primaryKey)) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.', + array('{class}'=>get_class($this->_parent->model), '{relation}'=>$relation->name))); + + $joinCondition=array(); + $map=array(); + foreach($fks as $i=>$fk) + { + if(!isset($joinTable->columns[$fk])) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".', + array('{class}'=>get_class($this->_parent->model), '{relation}'=>$relation->name, '{key}'=>$fk, '{table}'=>$joinTable->name))); + + if(isset($joinTable->foreignKeys[$fk])) + { + list($tableName,$pk)=$joinTable->foreignKeys[$fk]; + if(!isset($joinCondition[$pk]) && $schema->compareTableNames($table->rawName,$tableName)) + $joinCondition[$pk]=$table->rawName.'.'.$schema->quoteColumnName($pk).'='.$joinTable->rawName.'.'.$schema->quoteColumnName($fk); + else if(!isset($map[$pk]) && $schema->compareTableNames($pkTable->rawName,$tableName)) + $map[$pk]=$fk; + else + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.', + array('{class}'=>get_class($this->_parent->model), '{relation}'=>$relation->name, '{key}'=>$fk))); + } + else // FK constraints not defined + { + if($i<count($pkTable->primaryKey)) + { + $pk=is_array($pkTable->primaryKey) ? $pkTable->primaryKey[$i] : $pkTable->primaryKey; + $map[$pk]=$fk; + } + else + { + $j=$i-count($pkTable->primaryKey); + $pk=is_array($table->primaryKey) ? $table->primaryKey[$j] : $table->primaryKey; + $joinCondition[$pk]=$table->rawName.'.'.$schema->quoteColumnName($pk).'='.$joinTable->rawName.'.'.$schema->quoteColumnName($fk); + } + } + } + + if($joinCondition===array() || $map===array()) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.', + array('{class}'=>get_class($this->_parent->model), '{relation}'=>$relation->name))); + + $records=$this->_parent->records; + + $cols=array(); + foreach(is_string($pkTable->primaryKey)?array($pkTable->primaryKey):$pkTable->primaryKey as $n=>$pk) + { + $name=$joinTable->rawName.'.'.$schema->quoteColumnName($map[$pk]); + $cols[$name]=$name.' AS '.$schema->quoteColumnName('c'.$n); + } + + $keys=array_keys($records); + if(is_array($pkTable->primaryKey)) + { + foreach($keys as &$key) + { + $key2=unserialize($key); + $key=array(); + foreach($pkTable->primaryKey as $pk) + $key[$map[$pk]]=$key2[$pk]; + } + } + + $where=empty($relation->condition)?'' : ' WHERE ('.$relation->condition.')'; + $group=empty($relation->group)?'' : ', '.$relation->group; + $having=empty($relation->having)?'' : ' AND ('.$relation->having.')'; + $order=empty($relation->order)?'' : ' ORDER BY '.$relation->order; + + $sql='SELECT '.$this->relation->select.' AS '.$schema->quoteColumnName('s').', '.implode(', ',$cols) + .' FROM '.$table->rawName.' INNER JOIN '.$joinTable->rawName + .' ON ('.implode(') AND (',$joinCondition).')' + .$where + .' GROUP BY '.implode(', ',array_keys($cols)).$group + .' HAVING ('.$builder->createInCondition($joinTable,$map,$keys).')' + .$having.$order; + + $command=$builder->getDbConnection()->createCommand($sql); + if(is_array($relation->params)) + $builder->bindValues($command,$relation->params); + + $stats=array(); + foreach($command->queryAll() as $row) + { + if(is_array($pkTable->primaryKey)) + { + $key=array(); + foreach($pkTable->primaryKey as $n=>$k) + $key[$k]=$row['c'.$n]; + $stats[serialize($key)]=$row['s']; + } + else + $stats[$row['c0']]=$row['s']; + } + + foreach($records as $pk=>$record) + $record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$this->relation->defaultValue,false); + } +} diff --git a/web/framework-1.0.8/db/ar/CActiveRecord.php b/web/framework-1.0.8/db/ar/CActiveRecord.php new file mode 100644 index 0000000..e45d1b3 --- /dev/null +++ b/web/framework-1.0.8/db/ar/CActiveRecord.php @@ -0,0 +1,2212 @@ +<?php +/** + * CActiveRecord class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CActiveRecord is the base class for classes representing relational data. + * + * It implements the active record design pattern, a popular Object-Relational Mapping (ORM) technique. + * + * Each active record class represents a database table/view, while an active record instance + * represents a row in that table. Using CActiveRecord, one can perform CRUD + * (create, read, update, delete) database actions in an object-oriented way. For example, + * the following code inserts a row to the 'Post' table (where 'Post' is the class representing + * the 'Post' table): + * <pre> + * $post=new Post; + * $post->title='sample post'; + * $post->save(); + * </pre> + * + * In the following, we elaborate how to use CActiveRecord. + * + * First, we need to set up a database connection to be used by CActiveRecord. This is + * done by loading a {@link CDbConnection} application component whose ID is "db", using the following application + * configuration: + * <pre> + * array( + * 'components'=>array( + * 'db'=>array( + * 'class'=>'CDbConnection', + * 'connectionString'=>'sqlite:test.db', + * ), + * ) + * ); + * </pre> + * To use a different database connection, you should override {@link getDbConnection}. + * + * Second, for every database table that we want to access, define an active record class. + * The following example shows the minimal code needed for a 'Post' class, which represents + * the 'Post' table. + * <pre> + * class Post extends CActiveRecord + * { + * public static function model($className=__CLASS__) + * { + * return parent::model($className); + * } + * } + * </pre> + * The 'model()' method is declared as such for every active record class (to be explained shortly). + * By convention, the 'Post' class is associated with the database table named 'Post' (the class name). + * If the class should be associated with some other table, you may override + * the {@link tableName()} method. + * + * To access column values, use $record->columnName, where $record refers to an active record instance. + * For example, the following code sets the 'title' column (attribute) of $post: + * <pre> + * $post=new Post; + * $post->title='a sample post'; + * </pre> + * Although we never explicitly declare the 'title' property in the 'Post' class, we can + * still access it in the above code. This is because 'title' is a column in the 'Post' table, + * and CActiveRecord makes it accessible as a property with the help of PHP __get() magic method. + * If you attempt to access a non-existing column in the same way, an exception will be thrown. + * + * To insert a new row into 'Post', we first create a new instance of 'Post' class, + * and then call 'save()' to do the insertion. + * <pre> + * $post=new Post; + * $post->title='sample post'; + * $post->save(); + * </pre> + * After insertion, the $post object will contain an updated primary key if it is auto-incremental. + * + * To query for posts, we will use the 'model' method we defined earlier on. The 'model' method + * is the only static method defined in CActiveRecord. It returns a static active record instance + * that is used to access class-level methods (something similar to static class methods). + * The following 'find' methods are all class-level methods that CActiveRecord implements to + * facilitate database querying: + * <pre> + * // find the first row satisfying the specified condition + * $post=Post::model()->find($condition,$params); + * + * // find all rows satisfying the specified condition + * $posts=Post::model()->findAll($condition,$params); + * + * // find the row with the specified primary key + * $post=Post::model()->findByPk($postID,$condition,$params); + * + * // find all rows with the specified primary keys + * $posts=Post::model()->findAllByPk($postIDs,$condition,$params); + * + * // find the row with the specified attribute values + * $post=Post::model()->findByAttributes($attributes,$condition,$params); + * + * // find all rows with the specified attribute values + * $posts=Post::model()->findAllByAttributes($attributes,$condition,$params); + * + * // find the first row using the specified SQL statement + * $post=Post::model()->findBySql($sql,$params); + * + * // find all rows using the specified SQL statement + * $posts=Post::model()->findAllBySql($sql,$params); + * + * // get the number of rows satisfying the specified condition + * $n=Post::model()->count($condition,$params); + * + * // get the number of rows using the specified SQL statement + * $n=Post::model()->countBySql($sql,$params); + * </pre> + * where $condition specifies the WHERE clause, and $params gives a list of parameters that + * should be bound to the generated SQL statement. + * You may also pass a {@link CDbCriteria} object at the place of $condition to specify + * more complex query conditions (in that case, $params will be ignored since you can sepcify it + * in the {@link CDbCriteria} object.) + * + * As we can see from the above, we have a set of find() methods and a set of findAll() methods. + * The result of the former is either an active record instance or null + * if no result is found, while the result of the latter is always an array. + * + * After obtaining an active record from query, we can update it or delete it. + * <pre> + * $post->title='new post title'; + * $post->save(); // or $post->delete(); + * </pre> + * In the above, we are using the same 'save()' method to do both insertion and update. + * CActiveRecord is intelligent enough to differentiate these two scenarios. + * + * CActiveRecord also has a few class-level methods to facilitate updating and deleting rows + * without instantiating active record objects. Below is a summary: + * <pre> + * // update all records with the specified attributes and condition + * Post::model()->updateAll($attributes,$condition,$params); + * + * // update one or several records with the specified primary key(s) and attribute values + * Post::model()->updateByPk($postID,$attributes,$condition,$params); + * + * // update one or several counter columns + * Post::model()->updateCounters($counters,$condition,$params); + * + * // delete all records with the specified condition + * Post::model()->deleteAll($condition,$params); + * + * // delete one or several records with the specified primary key(s) and attribute values + * Post::model()->deleteByPk($pk,$condition,$params); + * + * // check if a record exists with the specified condition + * Post::model()->exists($condition,$params); + * </pre> + * + * A very useful feature that CActiveRecord supports is retrieving related active records. + * An active record is often related with other active records, via the relationship + * defined between database tables. For example, a post belongs to an author and has many comments; + * a user has a profile; a post belongs to and has many categories. CActiveRecord makes retrieving + * these related objects very easy. + * + * Before retrieving related objects, we need to declare these relations in the class definition. + * This is done by overriding the {@link relations} method: + * <pre> + * class Post extends CActiveRecord + * { + * public function relations() + * { + * return array( + * 'author'=>array(self::BELONGS_TO, 'User', 'authorID'), + * 'comments'=>array(self::HAS_MANY, 'Comment', 'postID'), + * ); + * } + * } + * </pre> + * In the above, we declare two relations: + * <ul> + * <li>the 'author' relation is of BELONGS_TO type; it references the 'User' active record class; + * and the 'authorID' column of the 'Post' table is the foreign key.</li> + * <li>the 'comments' relation is of HAS_MANY type; it references the 'Comment' active record class; + * and the 'postID' column of the 'Comment' table is the foreign key.</li> + * </ul> + * + * Since the 'Post' class knows what kind of related objects it has, we can use + * the following code to access the author and comments of a post: + * <pre> + * $post=Post::model()->findByPk($postID); + * $author=$post->author; + * $comments=$post->comments; + * </pre> + * Internally, when we access 'author' or 'comments' the first time, an SQL query will + * be executed to fetch the corresponding active record(s). This is the so-called + * lazy loading, i.e., the related objects are loaded the first time when they are accessed. + * + * If we have 100 posts and would like to obtain the author and comments for every post, + * lazy loading would be very inefficient, because it means we will need 200 SQL queries + * in order to fetch those related objects. In this situation, we should resort to + * the so-called eager loading. + * <pre> + * $posts=Post::model()->with('author','comments')->findAll($condition,$params); + * </pre> + * Here we used the 'with' method to specify that we want to bring back post records + * together with their related author and comments. Internally, two SQL queries + * will be created and executed: one brings back the posts and their authors, and the other + * brings back the comments). The reason we do not fetch them all together in one shot is + * because the returned rows would otherwise contain many repetitive results (e.g. the post + * information is repeated for every comment it has), which is both inefficient for database + * server and PHP code. + * + * Eager loading can be nested. For example, if for every comment, we also want to know + * its author, we could use the following 'with' find: + * <pre> + * $posts=Post::model()->with(array('author','comments'=>'author'))->findAll($condition,$params); + * </pre> + * How many SQL queries do we need now? Still two (one for posts and their authors, and one + * for comments and their authors)! In fact, if there are N HAS_MANY/MANY_MANY relations involved, + * we would need N+1 queries in total. + * + * There is a minor inconvenience when using eager loading, though. That is, we need to + * disambiguate any column references in conditions or search criteria, because we are + * joining several tables together. In general, it would be safe if we prefix every column referenced + * in the condition or search criteria with the table name. + * + * Now let's describe more about the possible relations we can declare for active records. + * From database point of view, the relationship between two tables A and B has three types: + * one-to-many, one-to-one and many-to-many. In active records, these are classified into four types: + * <ul> + * <li>BELONGS_TO: if A vs. B is one-to-many, then B belongs to A (e.g. a post belongs to an author);</li> + * <li>HAS_MANY: if A vs. B is one-to-many, then A has many B (e.g. an author has many posts);</li> + * <li>HAS_ONE: this is special case of has-many where A has at most one B;</li> + * <li>MANY_MANY: this corresponds to many-to-many relationship in database. An intermediate + * join table is needed as most DBMS do not support many-to-many relationship directly. + * For example, a post can belong to many categories and a category can have many posts. + * We would use a table 'post_category' to join the 'posts' and 'categories' tables together.</li> + * </ul> + * + * We already see how to declare a relation in an active record class. In many situations, + * we often have more constraints on those related objects, such as the conditions that the related + * objects should satisfy, their orderings, etc. These can be specified as additional options + * in our relation declaration. For example, + * <pre> + * class Post extends CActiveRecord + * { + * ...... + * public function relations() + * { + * return array( + * 'comments'=>array(self::HAS_MANY,'Comment','postID', + * 'order'=>'??.createTime DESC', + * 'with'=>'author'), + * ); + * } + * } + * </pre> + * where the 'order' option specifies that the comments should be sorted by their creation time, + * and the 'with' option specifies that the comments should be loaded together with their authors. + * The special token '??.' is for disamibiguating the column reference. When SQL statement + * is generated, it will be replaced automatically by the table alias for the 'Comment' table. + * More detailed description about possible options can be found in {@link CActiveRelation} and + * {@link CHasManyRelation}. + * + * Since version 1.0.4, a new relational query, called STAT, is supported. It is meant + * to perform relational queries that return statistical information about related objects + * (e.g. the number of comments that each post has). To use this new relational query, + * we need to declare it in the {@link relations} method like other relations: + * <pre> + * class Post extends CActiveRecord + * { + * ...... + * public function relations() + * { + * return array( + * 'commentCount'=>array(self::STAT,'Comment','postID'), + * + * 'comments'=>array(self::HAS_MANY,'Comment','postID', + * 'order'=>'??.createTime DESC', + * 'with'=>'author'), + * ); + * } + * } + * </pre> + * In the above, we declare 'commentCount' to be a STAT relation that is related with + * the <code>Comment</code> model via its foreign key <code>postID</code>. Given a post + * model instance, we can then obtain the number of comments it has via the expression + * <code>$post->commentCount</code>. We can also obtain the number of comments for a list + * of posts via eager loading: + * <pre> + * $posts=Post::model()->with('commentCount')->findAll(); + * </pre> + * + * + * CActiveRecord has built-in validation functionality that validates the user input data + * before they are saved to database. To use the validation, override {@link CModel::rules()} as follows, + * <pre> + * class Post extends CActiveRecord + * { + * public function rules() + * { + * return array( + * array('title, content', 'required'), + * array('title', 'length', 'min'=>5), + * ); + * } + * } + * </pre> + * The method returns a list of validation rules, each represented as an array of the following format: + * <pre> + * array('attribute list', 'validator name', 'on'=>'insert', ...validation parameters...) + * </pre> + * where + * <ul> + * <li>attribute list: specifies the attributes to be validated;</li> + * <li>validator name: specifies the validator to be used. It can be a class name (or class in dot syntax), + * or a validation method in the AR class. A validator class must extend from {@link CValidator}, + * while a validation method must have the following signature: + * <pre> + * function validatorName($attribute,$params) + * </pre> + * When using a built-in validator class, you can use an alias name instead of the full class name. + * For example, you can use "required" instead of "system.validators.CRequiredValidator". + * For more details, see {@link CValidator}.</li> + * <li>on: this specifies the scenario when the validation rule should be performed. Please see {@link CModel::validate} + * for more details about this option. NOTE: if the validation is triggered by the {@link save} call, + * it will use 'insert' to indicate an insertion operation, and 'update' an updating operation. + * You may thus specify a rule with the 'on' option so that it is applied only to insertion or updating.</li> + * <li>additional parameters are used to initialize the corresponding validator properties. See {@link CValidator} + * for possible properties.</li> + * </ul> + * + * When {@link save} is called, validation will be performed. If there are any validation errors, + * {@link save} will return false and the errors can be retrieved back via {@link getErrors}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +abstract class CActiveRecord extends CModel +{ + const BELONGS_TO='CBelongsToRelation'; + const HAS_ONE='CHasOneRelation'; + const HAS_MANY='CHasManyRelation'; + const MANY_MANY='CManyManyRelation'; + const STAT='CStatRelation'; + + /** + * @var CDbConnection the default database connection for all active record classes. + * By default, this is the 'db' application component. + * @see getDbConnection + */ + public static $db; + + private static $_models=array(); // class name => model + + private $_md; // meta data + private $_new=false; // whether this instance is new or not + private $_attributes=array(); // attribute name => attribute value + private $_related=array(); // attribute name => related objects + private $_c; // query criteria (used by finder only) + + + /** + * Constructor. + * @param array initial attributes (name => value). The attributes + * are subject to filtering via {@link setAttributes}. If this parameter is null, + * nothing will be done in the constructor (this is internally used by {@link populateRecord}). + * @param string scenario name. See {@link setAttributes} for more details about this parameter. + * This parameter has been available since version 1.0.2. + * As of version 1.0.4, this parameter will be used to set the {@link CModel::scenario scenario} + * property of the model. + * @see setAttributes + */ + public function __construct($attributes=array(),$scenario='') + { + if($attributes===null) // internally used by populateRecord() and model() + return; + + $this->setScenario($scenario); + $this->setIsNewRecord(true); + $this->_attributes=$this->getMetaData()->attributeDefaults; + + $this->init(); + + if($attributes!==array()) + $this->setAttributes($attributes); + + $this->attachBehaviors($this->behaviors()); + $this->afterConstruct(); + } + + /** + * Initializes this model. + * This method is invoked in the constructor right after {@link scenario} is set. + * You may override this method to provide code that is needed to initialize the model (e.g. setting + * initial property values.) + * @since 1.0.8 + */ + public function init() + { + } + + /** + * PHP sleep magic method. + * This method ensures that the model meta data reference is set to null. + */ + public function __sleep() + { + $this->_md=null; + return array_keys((array)$this); + } + + /** + * PHP getter magic method. + * This method is overridden so that AR attributes can be accessed like properties. + * @param string property name + * @return mixed property value + * @see getAttribute + */ + public function __get($name) + { + if(isset($this->_attributes[$name])) + return $this->_attributes[$name]; + else if(isset($this->getMetaData()->columns[$name])) + return null; + else if(isset($this->_related[$name])) + return $this->_related[$name]; + else if(isset($this->getMetaData()->relations[$name])) + return $this->getRelated($name); + else + return parent::__get($name); + } + + /** + * PHP setter magic method. + * This method is overridden so that AR attributes can be accessed like properties. + * @param string property name + * @param mixed property value + */ + public function __set($name,$value) + { + if($this->setAttribute($name,$value)===false) + { + if(isset($this->getMetaData()->relations[$name])) + $this->_related[$name]=$value; + else + parent::__set($name,$value); + } + } + + /** + * Checks if a property value is null. + * This method overrides the parent implementation by checking + * if the named attribute is null or not. + * @param string the property name or the event name + * @return boolean whether the property value is null + * @since 1.0.1 + */ + public function __isset($name) + { + if(isset($this->_attributes[$name])) + return true; + else if(isset($this->getMetaData()->columns[$name])) + return false; + else if(isset($this->_related[$name])) + return true; + else if(isset($this->getMetaData()->relations[$name])) + return $this->getRelated($name)!==null; + else + return parent::__isset($name); + } + + /** + * Sets a component property to be null. + * This method overrides the parent implementation by clearing + * the specified attribute value. + * @param string the property name or the event name + * @since 1.0.1 + */ + public function __unset($name) + { + if(isset($this->getMetaData()->columns[$name])) + unset($this->_attributes[$name]); + else if(isset($this->getMetaData()->relations[$name])) + unset($this->_related[$name]); + else + parent::__unset($name); + } + + /** + * Calls the named method which is not a class method. + * Do not call this method. This is a PHP magic method that we override + * to implement the named scope feature. + * @param string the method name + * @param array method parameters + * @return mixed the method return value + * @since 1.0.5 + */ + public function __call($name,$parameters) + { + if(isset($this->getMetaData()->relations[$name])) + { + if(empty($parameters)) + return $this->getRelated($name,false); + else + return $this->getRelated($name,false,$parameters[0]); + } + + $scopes=$this->scopes(); + if(isset($scopes[$name])) + { + $this->getDbCriteria()->mergeWith($scopes[$name]); + return $this; + } + + return parent::__call($name,$parameters); + } + + /** + * Returns the related record(s). + * This method will return the related record(s) of the current record. + * If the relation is HAS_ONE or BELONGS_TO, it will return a single object + * or null if the object does not exist. + * If the relation is HAS_MANY or MANY_MANY, it will return an array of objects + * or an empty array. + * @param string the relation name (see {@link relations}) + * @param boolean whether to reload the related objects from database. Defaults to false. + * @param array additional parameters that customize the query conditions as specified in the relation declaration. + * This parameter has been available since version 1.0.5. + * @return mixed the related object(s). + * @throws CDbException if the relation is not specified in {@link relations}. + * @since 1.0.2 + */ + public function getRelated($name,$refresh=false,$params=array()) + { + if(!$refresh && $params===array() && (isset($this->_related[$name]) || array_key_exists($name,$this->_related))) + return $this->_related[$name]; + + $md=$this->getMetaData(); + if(!isset($md->relations[$name])) + throw new CDbException(Yii::t('yii','{class} does not have relation "{name}".', + array('{class}'=>get_class($this), '{name}'=>$name))); + + Yii::trace('lazy loading '.get_class($this).'.'.$name,'system.db.ar.CActiveRecord'); + $relation=$md->relations[$name]; + if($this->getIsNewRecord() && ($relation instanceof CHasOneRelation || $relation instanceof CHasManyRelation)) + return $relation instanceof CHasOneRelation ? null : array(); + + if($params!==array()) // dynamic query + { + $exists=isset($this->_related[$name]) || array_key_exists($name,$this->_related); + if($exists) + $save=$this->_related[$name]; + unset($this->_related[$name]); + $r=array($name=>$params); + } + else + $r=$name; + + $finder=new CActiveFinder($this,$r); + $finder->lazyFind($this); + + if(!isset($this->_related[$name])) + { + if($relation instanceof CHasManyRelation) + $this->_related[$name]=array(); + else if($relation instanceof CStatRelation) + $this->_related[$name]=$relation->defaultValue; + else + $this->_related[$name]=null; + } + + if($params!==array()) + { + $results=$this->_related[$name]; + if($exists) + $this->_related[$name]=$save; + else + unset($this->_related[$name]); + return $results; + } + else + return $this->_related[$name]; + } + + /** + * Returns a value indicating whether the named related object(s) has been loaded. + * @param string the relation name + * @return booolean a value indicating whether the named related object(s) has been loaded. + * @since 1.0.3 + */ + public function hasRelated($name) + { + return isset($this->_related[$name]) || array_key_exists($name,$this->_related); + } + + /** + * Returns the query criteria associated with this model. + * @param boolean whether to create a criteria instance if it does not exist. Defaults to true. + * @return CDbCriteria the query criteria that is associated with this model. + * This criteria is mainly used by {@link scopes named scope} feature to accumulate + * different criteria specifications. + * @since 1.0.5 + */ + public function getDbCriteria($createIfNull=true) + { + if($this->_c===null) + { + if(($c=$this->defaultScope())!==array() || $createIfNull) + $this->_c=new CDbCriteria($c); + } + return $this->_c; + } + + /** + * Returns the default named scope that should be implicitly applied to all queries for this model. + * Note, default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries. + * The default implementation simply returns an empty array. You may override this method + * if the model needs to be queried with some default criteria (e.g. only active records should be returned). + * @return array the query criteria. This will be used as the parameter to the constructor + * of {@link CDbCriteria}. + * @since 1.0.5 + */ + public function defaultScope() + { + return array(); + } + + /** + * Returns the static model of the specified AR class. + * The model returned is a static instance of the AR class. + * It is provided for invoking class-level methods (something similar to static class methods.) + * + * EVERY derived AR class must override this method as follows, + * <pre> + * public static function model($className=__CLASS__) + * { + * return parent::model($className); + * } + * </pre> + * + * @param string active record class name. + * @return CActiveRecord active record model instance. + */ + public static function model($className=__CLASS__) + { + if(isset(self::$_models[$className])) + return self::$_models[$className]; + else + { + $model=self::$_models[$className]=new $className(null); + $model->attachBehaviors($model->behaviors()); + $model->_md=new CActiveRecordMetaData($model); + return $model; + } + } + + /** + * @return CActiveRecordMetaData the meta for this AR class. + */ + public function getMetaData() + { + if($this->_md!==null) + return $this->_md; + else + return $this->_md=self::model(get_class($this))->_md; + } + + /** + * Refreshes the meta data for this AR class. + * By calling this method, this AR class will regenerate the meta data needed. + * This is useful if the table schema has been changed and you want to use the latest + * available table schema. Make sure you have called {@link CDbSchema::refresh} + * before you call this method. Otherwise, old table schema data will still be used. + * @since 1.0.8 + */ + public function refreshMetaData() + { + $finder=self::model(get_class($this)); + $finder->_md=new CActiveRecordMetaData($finder); + if($this!==$finder) + $this->_md=$finder->_md; + } + + /** + * Returns the name of the associated database table. + * By default this method returns the class name as the table name. + * You may override this method if the table is not named after this convention. + * @return string the table name + */ + public function tableName() + { + return get_class($this); + } + + /** + * Returns the primary key of the associated database table. + * This method is meant to be overridden in case when the table is not defined with a primary key + * (for some legency database). If the table is already defined with a primary key, + * you do not need to override this method. The default implementation simply returns null, + * meaning using the primary key defined in the database. + * @return mixed the primary key of the associated database table. + * If the key is a single column, it should return the column name; + * If the key is a composite one consisting of several columns, it should + * return the array of the key column names. + * @since 1.0.4 + */ + public function primaryKey() + { + } + + /** + * This method should be overridden to declare related objects. + * + * There are four types of relations that may exist between two active record objects: + * <ul> + * <li>BELONGS_TO: e.g. a member belongs to a team;</li> + * <li>HAS_ONE: e.g. a member has at most one profile;</li> + * <li>HAS_MANY: e.g. a team has many members;</li> + * <li>MANY_MANY: e.g. a member has many skills and a skill belongs to a member.</li> + * </ul> + * + * By declaring these relations, CActiveRecord can bring back related objects + * in either lazy loading or eager loading approach, and you save the effort of + * writing complex JOIN SQL statements. + * + * Each kind of related objects is defined in this method as an array with the following elements: + * <pre> + * 'varName'=>array('relationType', 'className', 'foreignKey', ...additional options) + * </pre> + * where 'varName' refers to the name of the variable/property that the related object(s) can + * be accessed through; 'relationType' refers to the type of the relation, which can be one of the + * following four constants: self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY and self::MANY_MANY; + * 'className' refers to the name of the active record class that the related object(s) is of; + * and 'foreignKey' states the foreign key that relates the two kinds of active record. + * Note, for composite foreign keys, they must be listed together, separating with space or comma; + * and for foreign keys used in MANY_MANY relation, the joining table must be declared as well + * (e.g. 'joinTable(fk1, fk2)'). + * + * Additional options may be specified as name-value pairs in the rest array elements: + * <ul> + * <li>'select': string|array, a list of columns to be selected. Defaults to '*', meaning all columns. + * Column names should be disambiguated if they appear in an expression (e.g. COUNT(??.name) AS name_count).</li> + * <li>'condition': string, the WHERE clause. Defaults to empty. Note, column references need to + * be disambiguated with prefix '??.' (e.g. ??.age>20)</li> + * <li>'order': string, the ORDER BY clause. Defaults to empty. Note, column references need to + * be disambiguated with prefix '??.' (e.g. ??.age DESC)</li> + * <li>'with': string|array, a list of child related objects that should be loaded together with this object. + * Note, this is only honored by lazy loading, not eager loading.</li> + * <li>'joinType': type of join. Defaults to 'LEFT OUTER JOIN'.</li> + * <li>'aliasToken': the column prefix for column reference disambiguation. Defaults to '??.'.</li> + * <li>'alias': the alias for the table associated with this relationship. + * This option has been available since version 1.0.1. It defaults to null, + * meaning the table alias is automatically generated. This is different + * from `aliasToken` in that the latter is just a placeholder and will be + * replaced by the actual table alias.</li> + * <li>'params': the parameters to be bound to the generated SQL statement. + * This should be given as an array of name-value pairs. This option has been + * available since version 1.0.3.</li> + * <li>'on': the ON clause. The condition specified here will be appended + * to the joining condition using the AND operator. This option has been + * available since version 1.0.2.</li> + * <li>'index': the name of the column whose values should be used as keys + * of the array that stores related objects. This option is only available to + * HAS_MANY and MANY_MANY relations. This option has been available since version 1.0.7.</li> + * </ul> + * + * The following options are available for certain relations when lazy loading: + * <ul> + * <li>'group': string, the GROUP BY clause. Defaults to empty. Note, column references need to + * be disambiguated with prefix '??.' (e.g. ??.age). This option only applies to HAS_MANY and MANY_MANY relations.</li> + * <li>'having': string, the HAVING clause. Defaults to empty. Note, column references need to + * be disambiguated with prefix '??.' (e.g. ??.age). This option only applies to HAS_MANY and MANY_MANY relations.</li> + * <li>'limit': limit of the rows to be selected. This option does not apply to BELONGS_TO relation.</li> + * <li>'offset': offset of the rows to be selected. This option does not apply to BELONGS_TO relation.</li> + * </ul> + * + * Below is an example declaring related objects for 'Post' active record class: + * <pre> + * return array( + * 'author'=>array(self::BELONGS_TO, 'User', 'authorID'), + * 'comments'=>array(self::HAS_MANY, 'Comment', 'postID', 'with'=>'author', 'order'=>'createTime DESC'), + * 'tags'=>array(self::MANY_MANY, 'Tag', 'PostTag(postID, tagID)', 'order'=>'name'), + * ); + * </pre> + * + * @return array list of related object declarations. Defaults to empty array. + */ + public function relations() + { + return array(); + } + + /** + * Returns the declaration of named scopes. + * A named scope represents a query criteria that can be chained together with + * other named scopes and applied to a query. This method should be overridden + * by child classes to declare named scopes for the particular AR classes. + * For example, the following code declares two named scopes: 'recently' and + * 'published'. + * <pre> + * return array( + * 'published'=>array( + * 'condition'=>'status=1', + * ), + * 'recently'=>array( + * 'order'=>'createTime DESC', + * 'limit'=>5, + * ), + * ); + * </pre> + * If the above scopes are declared in a 'Post' model, we can perform the following + * queries: + * <pre> + * $posts=Post::model()->published()->findAll(); + * $posts=Post::model()->published()->recently()->findAll(); + * $posts=Post::model()->published()->with('comments')->findAll(); + * </pre> + * Note that the last query is a relational query. + * + * @return array the scope definition. The array keys are scope names; the array + * values are the corresponding scope definitions. Each scope definition is represented + * as an array whose keys must be properties of {@link CDbCriteria}. + * @since 1.0.5 + */ + public function scopes() + { + return array(); + } + + /** + * Returns the list of all attribute names of the model. + * This would return all column names of the table associated with this AR class. + * @return array list of attribute names. + * @since 1.0.1 + */ + public function attributeNames() + { + return array_keys($this->getMetaData()->columns); + } + + /** + * Returns the name of attributes that are safe to be massively assigned. + * The default implementation returns all table columns exception primary key(s). + * Child class may override this method to further limit the attributes that can be massively assigned. + * See {@link CModel::safeAttributes} on how to override this method. + * @return array list of safe attribute names. + * @see CModel::safeAttributes + */ + public function safeAttributes() + { + $attributes=array(); + foreach($this->getMetaData()->columns as $name=>$column) + { + if(!$column->isPrimaryKey) + $attributes[]=$name; + } + return $attributes; + } + + /** + * Returns the database connection used by active record. + * By default, the "db" application component is used as the database connection. + * You may override this method if you want to use a different database connection. + * @return CDbConnection the database connection used by active record. + */ + public function getDbConnection() + { + if(self::$db!==null) + return self::$db; + else + { + self::$db=Yii::app()->getDb(); + if(self::$db instanceof CDbConnection) + { + self::$db->setActive(true); + return self::$db; + } + else + throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.')); + } + } + + /** + * @param string the relation name + * @return CActiveRelation the named relation declared for this AR class. Null if the relation does not exist. + */ + public function getActiveRelation($name) + { + return isset($this->getMetaData()->relations[$name]) ? $this->getMetaData()->relations[$name] : null; + } + + /** + * @return CDbTableSchema the metadata of the table that this AR belongs to + */ + public function getTableSchema() + { + return $this->getMetaData()->tableSchema; + } + + /** + * @return CDbCommandBuilder the command builder used by this AR + */ + public function getCommandBuilder() + { + return $this->getDbConnection()->getSchema()->getCommandBuilder(); + } + + /** + * @param string attribute name + * @return boolean whether this AR has the named attribute (table column). + */ + public function hasAttribute($name) + { + return isset($this->getMetaData()->columns[$name]); + } + + /** + * Returns the named attribute value. + * If this is a new record and the attribute is not set before, + * the default column value will be returned. + * If this record is the result of a query and the attribute is not loaded, + * null will be returned. + * You may also use $this->AttributeName to obtain the attribute value. + * @param string the attribute name + * @return mixed the attribute value. Null if the attribute is not set or does not exist. + * @see hasAttribute + */ + public function getAttribute($name) + { + if(property_exists($this,$name)) + return $this->$name; + else if(isset($this->_attributes[$name])) + return $this->_attributes[$name]; + } + + /** + * Sets the named attribute value. + * You may also use $this->AttributeName to set the attribute value. + * @param string the attribute name + * @param mixed the attribute value. + * @return boolean whether the attribute exists and the assignment is conducted successfully + * @see hasAttribute + */ + public function setAttribute($name,$value) + { + if(property_exists($this,$name)) + $this->$name=$value; + else if(isset($this->getMetaData()->columns[$name])) + $this->_attributes[$name]=$value; + else + return false; + return true; + } + + /** + * Adds a related object to this record. + * This method is used internally by {@link CActiveFinder} to populate related objects. + * @param string attribute name + * @param mixed the related record + * @param mixed the index value in the related object collection. + * If true, it means using zero-based integer index. + * If false, it means a HAS_ONE or BELONGS_TO object and no index is needed. + */ + public function addRelatedRecord($name,$record,$index) + { + if($index!==false) + { + if(!isset($this->_related[$name])) + $this->_related[$name]=array(); + if($record instanceof CActiveRecord) + { + if($index===true) + $this->_related[$name][]=$record; + else + $this->_related[$name][$index]=$record; + } + } + else if(!isset($this->_related[$name])) + $this->_related[$name]=$record; + } + + /** + * Returns all column attribute values. + * Note, related objects are not returned. + * @param mixed names of attributes whose value needs to be returned. + * If this is true (default), then all attribute values will be returned, including + * those that are not loaded from DB (null will be returned for those attributes). + * If this is null, all attributes except those that are not loaded from DB will be returned. + * @return array attribute values indexed by attribute names. + */ + public function getAttributes($names=true) + { + $attributes=$this->_attributes; + foreach($this->getMetaData()->columns as $name=>$column) + { + if(property_exists($this,$name)) + $attributes[$name]=$this->$name; + else if($names===true && !isset($attributes[$name])) + $attributes[$name]=null; + } + if(is_array($names)) + { + $attrs=array(); + foreach($names as $name) + $attrs[$name]=isset($attributes[$name])?$attributes[$name]:null; + return $attrs; + } + else + return $attributes; + } + + /** + * Saves the current record. + * The record is inserted as a row into the database table if it is manually + * created using the 'new' operator. If it is obtained using one of those + * 'find' methods, the record is considered not new and it will be used to + * update the corresponding row in the table. You may check this status via {@link isNewRecord}. + * Validation may be performed before saving the record. If the validation fails, + * the record will not be saved. + * If the record is being inserted and its primary key is null, + * after insertion the primary key will be populated with the value + * generated automatically by the database. + * @param boolean whether to perform validation before saving the record. + * If the validation fails, the record will not be saved to database. + * The validation will be performed under either 'insert' or 'update' scenario, + * depending on whether {@link isNewRecord} is true or false. + * @param array list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the saving succeeds + */ + public function save($runValidation=true,$attributes=null) + { + if(($scenario=$this->getScenario())==='') + $scenario=$this->getIsNewRecord()?'insert':'update'; + if(!$runValidation || $this->validate($scenario,$attributes)) + return $this->getIsNewRecord() ? $this->insert($attributes) : $this->update($attributes); + else + return false; + } + + /** + * @return boolean whether the record is new and should be inserted when calling {@link save}. + * This property is automatically set in constructor and {@link populateRecord}. + * Defaults to false, but it will be set to true if the instance is created using + * the new operator. + */ + public function getIsNewRecord() + { + return $this->_new; + } + + /** + * @param boolean whether the record is new and should be inserted when calling {@link save}. + * @see getIsNewRecord + */ + public function setIsNewRecord($value) + { + $this->_new=$value; + if(!$value && $this->getScenario()==='insert') + $this->setScenario('update'); + } + + /** + * Returns a list of validators created according to {@link CModel::rules rules}. + * This overrides the parent implementation so that the validators are only + * created once for each type of AR. + * @return array a list of validators created according to {@link CModel::rules}. + * @since 1.0.1 + */ + public function getValidators() + { + return $this->getMetaData()->getValidators(); + } + + /** + * This event is raised before the record is saved. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onBeforeSave($event) + { + $this->raiseEvent('onBeforeSave',$event); + } + + /** + * This event is raised after the record is saved. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onAfterSave($event) + { + $this->raiseEvent('onAfterSave',$event); + } + + /** + * This event is raised before the record is deleted. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onBeforeDelete($event) + { + $this->raiseEvent('onBeforeDelete',$event); + } + + /** + * This event is raised after the record is deleted. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onAfterDelete($event) + { + $this->raiseEvent('onAfterDelete',$event); + } + + /** + * This event is raised after the record instance is created by new operator. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onAfterConstruct($event) + { + $this->raiseEvent('onAfterConstruct',$event); + } + + /** + * This event is raised after the record is instantiated by a find method. + * @param CEvent the event parameter + * @since 1.0.2 + */ + public function onAfterFind($event) + { + $this->raiseEvent('onAfterFind',$event); + } + + /** + * This method is invoked before saving a record (after validation, if any). + * The default implementation raises the {@link onBeforeSave} event. + * You may override this method to do any preparation work for record saving. + * Use {@link isNewRecord} to determine whether the saving is + * for inserting or updating record. + * Make sure you call the parent implementation so that the event is raised properly. + * @return boolean whether the saving should be executed. Defaults to true. + */ + protected function beforeSave() + { + $event=new CModelEvent($this); + $this->onBeforeSave($event); + return $event->isValid; + } + + /** + * This method is invoked after saving a record. + * The default implementation raises the {@link onAfterSave} event. + * You may override this method to do postprocessing after record saving. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterSave() + { + $this->onAfterSave(new CEvent($this)); + } + + /** + * This method is invoked before deleting a record. + * The default implementation raises the {@link onBeforeDelete} event. + * You may override this method to do any preparation work for record deletion. + * Make sure you call the parent implementation so that the event is raised properly. + * @return boolean whether the record should be deleted. Defaults to true. + */ + protected function beforeDelete() + { + $event=new CModelEvent($this); + $this->onBeforeDelete($event); + return $event->isValid; + } + + /** + * This method is invoked after deleting a record. + * The default implementation raises the {@link onAfterDelete} event. + * You may override this method to do postprocessing after the record is deleted. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterDelete() + { + $this->onAfterDelete(new CEvent($this)); + } + + /** + * This method is invoked after a record instance is created by new operator. + * The default implementation raises the {@link onAfterConstruct} event. + * You may override this method to do postprocessing after record creation. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterConstruct() + { + $this->onAfterConstruct(new CEvent($this)); + } + + /** + * This method is invoked after each record is instantiated by a find method. + * The default implementation raises the {@link onAfterFind} event. + * You may override this method to do postprocessing after each newly found record is instantiated. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterFind() + { + $this->onAfterFind(new CEvent($this)); + } + + /** + * Calls {@link afterFind}. + * This method is internally used. + * @since 1.0.3 + */ + public function afterFindInternal() + { + $this->afterFind(); + } + + /** + * Inserts a row into the table based on this active record attributes. + * If the table's primary key is auto-incremental and is null before insertion, + * it will be populated with the actual value after insertion. + * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. + * @param array list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the attributes are valid and the record is inserted successfully. + * @throws CException if the record is not new + */ + public function insert($attributes=null) + { + if(!$this->getIsNewRecord()) + throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new.')); + if($this->beforeSave()) + { + Yii::trace(get_class($this).'.insert()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getMetaData()->tableSchema; + $command=$builder->createInsertCommand($table,$this->getAttributes($attributes)); + if($command->execute()) + { + $primaryKey=$table->primaryKey; + if($table->sequenceName!==null) + { + if(is_string($primaryKey) && $this->$primaryKey===null) + $this->$primaryKey=$builder->getLastInsertID($table); + else if(is_array($primaryKey)) + { + foreach($primaryKey as $pk) + { + if($this->$pk===null) + { + $this->$pk=$builder->getLastInsertID($table); + break; + } + } + } + } + $this->afterSave(); + $this->setIsNewRecord(false); + return true; + } + else + $this->afterSave(); + } + else + return false; + } + + /** + * Updates the row represented by this active record. + * All loaded attributes will be saved to the database. + * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. + * @param array list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the update is successful + * @throws CException if the record is new + */ + public function update($attributes=null) + { + if($this->getIsNewRecord()) + throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); + if($this->beforeSave()) + { + Yii::trace(get_class($this).'.update()','system.db.ar.CActiveRecord'); + $this->updateByPk($this->getPrimaryKey(),$this->getAttributes($attributes)); + $this->afterSave(); + return true; + } + else + return false; + } + + /** + * Saves a selected list of attributes. + * Unlike {@link save}, this method only saves the specified attributes + * of an existing row dataset. It thus has better performance. + * Note, this method does neither attribute filtering nor validation. + * So do not use this method with untrusted data (such as user posted data). + * You may consider the following alternative if you want to do so: + * <pre> + * $postRecord=Post::model()->findByPk($postID); + * $postRecord->attributes=$_POST['post']; + * $postRecord->save(); + * </pre> + * @param array attributes to be updated. Each element represents an attribute name + * or an attribute value indexed by its name. If the latter, the record's + * attribute will be changed accordingly before saving. + * @return boolean whether the update is successful + * @throws CException if the record is new or any database error + */ + public function saveAttributes($attributes) + { + if(!$this->getIsNewRecord()) + { + Yii::trace(get_class($this).'.saveAttributes()','system.db.ar.CActiveRecord'); + $values=array(); + foreach($attributes as $name=>$value) + { + if(is_integer($name)) + $values[$value]=$this->$value; + else + $values[$name]=$this->$name=$value; + } + return $this->updateByPk($this->getPrimaryKey(),$values)>0; + } + else + throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); + } + + /** + * Deletes the row corresponding to this active record. + * @return boolean whether the deletion is successful. + * @throws CException if the record is new + */ + public function delete() + { + if(!$this->getIsNewRecord()) + { + Yii::trace(get_class($this).'.delete()','system.db.ar.CActiveRecord'); + if($this->beforeDelete()) + { + $result=$this->deleteByPk($this->getPrimaryKey())>0; + $this->afterDelete(); + return $result; + } + else + return false; + } + else + throw new CDbException(Yii::t('yii','The active record cannot be deleted because it is new.')); + } + + /** + * Repopulates this active record with the latest data. + * @return boolean whether the row still exists in the database. If true, the latest data will be populated to this active record. + */ + public function refresh() + { + Yii::trace(get_class($this).'.refresh()','system.db.ar.CActiveRecord'); + if(!$this->getIsNewRecord() && ($record=$this->findByPk($this->getPrimaryKey()))!==null) + { + $this->_attributes=array(); + $this->_related=array(); + foreach($this->getMetaData()->columns as $name=>$column) + $this->$name=$record->$name; + return true; + } + else + return false; + } + + /** + * Compares this active record with another one. + * The comparison is made by comparing the primary key values of the two active records. + * @return boolean whether the two active records refer to the same row in the database table. + */ + public function equals($record) + { + return $this->tableName()===$record->tableName() && $this->getPrimaryKey()===$record->getPrimaryKey(); + } + + /** + * @return mixed the primary key value. An array (column name=>column value) is returned if the primary key is composite. + * If primary key is not defined, null will be returned. + */ + public function getPrimaryKey() + { + $table=$this->getMetaData()->tableSchema; + if(is_string($table->primaryKey)) + return $this->{$table->primaryKey}; + else if(is_array($table->primaryKey)) + { + $values=array(); + foreach($table->primaryKey as $name) + $values[$name]=$this->$name; + return $values; + } + else + return null; + } + + private function query($criteria,$all=false) + { + $this->applyScopes($criteria); + $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria); + return $all ? $this->populateRecords($command->queryAll()) : $this->populateRecord($command->queryRow()); + } + + private function applyScopes(&$criteria) + { + if(($c=$this->getDbCriteria(false))!==null) + { + $c->mergeWith($criteria); + $criteria=$c; + $this->_c=null; + } + } + + /** + * Finds a single active record with the specified condition. + * @param mixed query condition or criteria. + * If a string, it is treated as query condition (the WHERE clause); + * If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object; + * Otherwise, it should be an instance of {@link CDbCriteria}. + * @param array parameters to be bound to an SQL statement. + * This is only used when the first parameter is a string (query condition). + * In other cases, please use {@link CDbCriteria::params} to set parameters. + * @return CActiveRecord the record found. Null if no record is found. + */ + public function find($condition='',$params=array()) + { + Yii::trace(get_class($this).'.find()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); + $criteria->limit=1; + return $this->query($criteria); + } + + /** + * Finds all active records satisfying the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return array list of active records satisfying the specified condition. An empty array is returned if none is found. + */ + public function findAll($condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAll()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); + return $this->query($criteria,true); + } + + /** + * Finds a single active record with the specified primary key. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return CActiveRecord the record found. Null if none is found. + */ + public function findByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findByPk()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params); + $criteria->limit=1; + return $this->query($criteria); + } + + /** + * Finds all active records with the specified primary keys. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return array the records found. An empty array is returned if none is found. + */ + public function findAllByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAllByPk()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params); + return $this->query($criteria,true); + } + + /** + * Finds a single active record that has the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array list of attribute values (indexed by attribute names) that the active records should match. + * Since version 1.0.8, an attribute value can be an array which will be used to generate an IN condition. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return CActiveRecord the record found. Null if none is found. + */ + public function findByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findByAttributes()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params); + $criteria->limit=1; + return $this->query($criteria); + } + + /** + * Finds all active records that have the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array list of attribute values (indexed by attribute names) that the active records should match. + * Since version 1.0.8, an attribute value can be an array which will be used to generate an IN condition. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return array the records found. An empty array is returned if none is found. + */ + public function findAllByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAllByAttributes()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params); + return $this->query($criteria,true); + } + + /** + * Finds a single active record with the specified SQL statement. + * @param string the SQL statement + * @param array parameters to be bound to the SQL statement + * @return CActiveRecord the record found. Null if none is found. + */ + public function findBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.findBySql()','system.db.ar.CActiveRecord'); + $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); + return $this->populateRecord($command->queryRow()); + } + + /** + * Finds all active records using the specified SQL statement. + * @param string the SQL statement + * @param array parameters to be bound to the SQL statement + * @return array the records found. An empty array is returned if none is found. + */ + public function findAllBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.findAllBySql()','system.db.ar.CActiveRecord'); + $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); + return $this->populateRecords($command->queryAll()); + } + + /** + * Finds the number of rows satisfying the specified query condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows satisfying the specified query condition. + */ + public function count($condition='',$params=array()) + { + Yii::trace(get_class($this).'.count()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $this->applyScopes($criteria); + return $builder->createCountCommand($this->getTableSchema(),$criteria)->queryScalar(); + } + + /** + * Finds the number of rows using the given SQL statement. + * @param string the SQL statement + * @param array parameters to be bound to the SQL statement + * @return integer the number of rows using the given SQL statement. + */ + public function countBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.countBySql()','system.db.ar.CActiveRecord'); + return $this->getCommandBuilder()->createSqlCommand($sql,$params)->queryScalar(); + } + + /** + * Checks whether there is row satisfying the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return boolean whether there is row satisfying the specified condition. + */ + public function exists($condition,$params=array()) + { + Yii::trace(get_class($this).'.exists()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $table=$this->getTableSchema(); + $criteria->select=reset($table->columns)->rawName; + $criteria->limit=1; + $this->applyScopes($criteria); + return $builder->createFindCommand($table,$criteria)->queryRow()!==false; + } + + /** + * Specifies which related objects should be eagerly loaded. + * This method takes variable number of parameters. Each parameter specifies + * the name of a relation or child-relation. For example, + * <pre> + * // find all posts together with their author and comments + * Post::model()->with('author','comments')->findAll(); + * // find all posts together with their author and the author's profile + * Post::model()->with('author','author.profile')->findAll(); + * </pre> + * The relations should be declared in {@link relations()}. + * + * By default, the options specified in {@link relations()} will be used + * to do relational query. In order to customize the options on the fly, + * we should pass an array parameter to the with() method. The array keys + * are relation names, and the array values are the corresponding query options. + * For example, + * <pre> + * Post::model()->with(array( + * 'author'=>array('select'=>'id, name'), + * 'comments'=>array('condition'=>'approved=1', 'order'=>'createTime'), + * ))->findAll(); + * </pre> + * + * This method returns a {@link CActiveFinder} instance that provides + * a set of find methods similar to that of CActiveRecord. + * + * Note, the possible parameters to this method have been changed since version 1.0.2. + * Previously, it was not possible to specify on-th-fly query options, + * and child-relations were specified as hierarchical arrays. + * + * @return CActiveFinder the active finder instance. If no parameter is passed in, the object itself will be returned. + */ + public function with() + { + if(func_num_args()>0) + { + $with=func_get_args(); + if(is_array($with[0])) // the parameter is given as an array + $with=$with[0]; + $finder=new CActiveFinder($this,$with,$this->getDbCriteria(false)); + $this->_c=null; + return $finder; + } + else + return $this; + } + + /** + * Updates records with the specified primary key(s). + * See {@link find()} for detailed explanation about $condition and $params. + * Note, the attributes are not checked for safety and validation is NOT performed. + * @param mixed primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param array list of attributes (name=>$value) to be updated + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + */ + public function updateByPk($pk,$attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateByPk()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getTableSchema(); + $criteria=$builder->createPkCriteria($table,$pk,$condition,$params); + $command=$builder->createUpdateCommand($table,$attributes,$criteria); + return $command->execute(); + } + + /** + * Updates records with the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * Note, the attributes are not checked for safety and no validation is done. + * @param array list of attributes (name=>$value) to be updated + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + */ + public function updateAll($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateAll()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createUpdateCommand($this->getTableSchema(),$attributes,$criteria); + return $command->execute(); + } + + /** + * Updates one or several counter columns. + * Note, this updates all rows of data unless a condition or criteria is specified. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array the counters to be updated (column name=>increment value) + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + */ + public function updateCounters($counters,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateCounters()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createUpdateCounterCommand($this->getTableSchema(),$counters,$criteria); + return $command->execute(); + } + + /** + * Deletes rows with the specified primary key. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows deleted + */ + public function deleteByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.deleteByPk()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createPkCriteria($this->getTableSchema(),$pk,$condition,$params); + $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); + return $command->execute(); + } + + /** + * Deletes rows with the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed query condition or criteria. + * @param array parameters to be bound to an SQL statement. + * @return integer the number of rows deleted + */ + public function deleteAll($condition='',$params=array()) + { + Yii::trace(get_class($this).'.deleteAll()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); + return $command->execute(); + } + + /** + * Creates an active record with the given attributes. + * This method is internally used by the find methods. + * @param array attribute values (column name=>column value) + * @param boolean whether to call {@link afterFind} after the record is populated. + * This parameter is added in version 1.0.3. + * @return CActiveRecord the newly created active record. The class of the object is the same as the model class. + * Null is returned if the input data is false. + */ + public function populateRecord($attributes,$callAfterFind=true) + { + if($attributes!==false) + { + $record=$this->instantiate($attributes); + $record->_md=$this->getMetaData(); + foreach($attributes as $name=>$value) + { + if(property_exists($record,$name)) + $record->$name=$value; + else if(isset($record->_md->columns[$name])) + $record->_attributes[$name]=$value; + } + $record->attachBehaviors($record->behaviors()); + if($callAfterFind) + $record->afterFind(); + return $record; + } + else + return null; + } + + /** + * Creates a list of active records based on the input data. + * This method is internally used by the find methods. + * @param array list of attribute values for the active records. + * @param boolean whether to call {@link afterFind} after each record is populated. + * This parameter is added in version 1.0.3. + * @return array list of active records. + */ + public function populateRecords($data,$callAfterFind=true) + { + $records=array(); + $md=$this->getMetaData(); + $table=$md->tableSchema; + foreach($data as $attributes) + { + $record=$this->instantiate($attributes); + $record->_md=$md; + foreach($attributes as $name=>$value) + { + if(property_exists($record,$name)) + $record->$name=$value; + else if(isset($record->_md->columns[$name])) + $record->_attributes[$name]=$value; + } + $record->attachBehaviors($record->behaviors()); + if($callAfterFind) + $record->afterFind(); + $records[]=$record; + } + return $records; + } + + /** + * Creates an active record instance. + * This method is called by {@link populateRecord} and {@link populateRecords}. + * You may override this method if the instance being created + * depends the attributes that are to be populated to the record. + * For example, by creating a record based on the value of a column, + * you may implement the so-called single-table inheritance mapping. + * @param array list of attribute values for the active records. + * @return CActiveRecord the active record + * @since 1.0.2 + */ + protected function instantiate($attributes) + { + $class=get_class($this); + return new $class(null); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + * @since 1.0.2 + */ + public function offsetExists($offset) + { + return isset($this->getMetaData()->columns[$offset]); + } +} + + +/** + * CBaseActiveRelation is the base class for all active relations. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0.4 + */ +class CBaseActiveRelation extends CComponent +{ + /** + * @var string name of the related object + */ + public $name; + /** + * @var string name of the related active record class + */ + public $className; + /** + * @var string the foreign key in this relation + */ + public $foreignKey; + /** + * @var mixed list of column names (an array, or a string of names separated by commas) to be selected. + * Do not quote or prefix the column names unless they are used in an expression. + * In that case, you should prefix the column names with '??.'. + */ + public $select='*'; + /** + * @var string WHERE clause. For {@link CActiveRelation} descendant classes, column names + * referenced in the condition should be disambiguated with prefix '??.'. + */ + public $condition=''; + /** + * @var array the parameters that are to be bound to the condition. + * The keys are parameter placeholder names, and the values are parameter values. + */ + public $params=array(); + /** + * @var string GROUP BY clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix '??.'. + */ + public $group=''; + /** + * @var string HAVING clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix '??.'. + */ + public $having=''; + /** + * @var string ORDER BY clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix '??.'. + */ + public $order=''; + + /** + * Constructor. + * @param string name of the relation + * @param string name of the related active record class + * @param string foreign key for this relation + * @param array additional options (name=>value). The keys must be the property names of this class. + */ + public function __construct($name,$className,$foreignKey,$options=array()) + { + $this->name=$name; + $this->className=$className; + $this->foreignKey=$foreignKey; + foreach($options as $name=>$value) + $this->$name=$value; + } + + /** + * Merges this relation with a criteria specified dynamically. + * @param array the dynamically specified criteria + * @since 1.0.5 + */ + public function mergeWith($criteria) + { + if(isset($criteria['select']) && $this->select!==$criteria['select']) + { + if($this->select==='*') + $this->select=$criteria['select']; + else if($criteria['select']!=='*') + { + $select1=is_string($this->select)?preg_split('/\s*,\s*/',trim($this->select),-1,PREG_SPLIT_NO_EMPTY):$this->select; + $select2=is_string($criteria['select'])?preg_split('/\s*,\s*/',trim($criteria['select']),-1,PREG_SPLIT_NO_EMPTY):$criteria['select']; + $this->select=array_merge($select1,array_diff($select2,$select1)); + } + } + + if(isset($criteria['condition']) && $this->condition!==$criteria['condition']) + { + if($this->condition==='') + $this->condition=$criteria['condition']; + else if($criteria['condition']!=='') + $this->condition="({$this->condition}) AND ({$criteria['condition']})"; + } + + if(isset($criteria['params']) && $this->params!==$criteria['params']) + $this->params=array_merge($this->params,$criteria['params']); + + if(isset($criteria['order']) && $this->order!==$criteria['order']) + { + if($this->order==='') + $this->order=$criteria['order']; + else if($criteria['order']!=='') + $this->order.=', '.$criteria['order']; + } + + if(isset($criteria['group']) && $this->group!==$criteria['group']) + { + if($this->group==='') + $this->group=$criteria['group']; + else if($criteria['group']!=='') + $this->group.=', '.$criteria['group']; + } + + if(isset($criteria['having']) && $this->having!==$criteria['having']) + { + if($this->having==='') + $this->having=$criteria['having']; + else if($criteria['having']!=='') + $this->having="({$this->having}) AND ({$criteria['having']})"; + } + } +} + + +/** + * CStatRelation represents a statistical relational query. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0.4 + */ +class CStatRelation extends CBaseActiveRelation +{ + /** + * @var string the statistical expression. Defaults to 'COUNT(*)', meaning + * the count of child objects. + */ + public $select='COUNT(*)'; + /** + * @var mixed the default value to be assigned to those records that do not + * receive a statistical query result. Defaults to 0. + */ + public $defaultValue=0; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array the dynamically specified criteria + * @since 1.0.5 + */ + public function mergeWith($criteria) + { + parent::mergeWith($criteria); + + if(isset($criteria['defaultValue'])) + $this->defaultValue=$criteria['defaultValue']; + } +} + + +/** + * CActiveRelation is the base class for representing active relations that bring back related objects. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CActiveRelation extends CBaseActiveRelation +{ + /** + * @var string join type. Defaults to 'LEFT OUTER JOIN'. + */ + public $joinType='LEFT OUTER JOIN'; + /** + * @var string ON clause. The condition specified here will be appended to the joining condition using AND operator. + * @since 1.0.2 + */ + public $on=''; + /** + * @var string the alias for the table that this relation refers to. Defaults to null, meaning + * the alias will be generated automatically. If you set this property explicitly, make sure + * the alias is unique globally. + * @see aliasToken + * @since 1.0.1 + */ + public $alias; + /** + * @var string the column prefix placeholder. It will be replaced by the corresponding table alias. + * @see alias + */ + public $aliasToken='??'; + /** + * @var string|array specifies which related objects should be eagerly loaded when this related object is lazily loaded. + * For more details about this property, see {@link CActiveRecord::with()}. + */ + public $with=array(); + /** + * @var boolean whether this table should be joined with the primary table. If not set or false, + * Each HAS_MANY or MANY_MANY table will appear in a separate JOIN statement. Defaults to null. + * @since 1.0.3 + */ + public $together; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array the dynamically specified criteria + * @since 1.0.5 + */ + public function mergeWith($criteria) + { + parent::mergeWith($criteria); + + if(isset($criteria['joinType'])) + $this->joinType=$criteria['joinType']; + + if(isset($criteria['on']) && $this->on!==$criteria['on']) + { + if($this->on==='') + $this->on=$criteria['on']; + else if($criteria['on']!=='') + $this->on="({$this->on}) AND ({$criteria['on']})"; + } + + if(isset($criteria['with'])) + $this->with=$criteria['with']; + + if(isset($criteria['alias'])) + $this->alias=$criteria['alias']; + + if(isset($criteria['aliasToken'])) + $this->aliasToken=$criteria['aliasToken']; + + if(isset($criteria['together'])) + $this->together=$criteria['together']; + } +} + + +/** + * CBelongsToRelation represents the parameters specifying a BELONGS_TO relation. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CBelongsToRelation extends CActiveRelation +{ +} + + +/** + * CHasOneRelation represents the parameters specifying a HAS_ONE relation. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CHasOneRelation extends CActiveRelation +{ +} + + +/** + * CHasManyRelation represents the parameters specifying a HAS_MANY relation. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CHasManyRelation extends CActiveRelation +{ + /** + * @var integer limit of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no limit. + */ + public $limit=-1; + /** + * @var integer offset of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no offset. + */ + public $offset=-1; + /** + * @var string the name of the column that should be used as the key for storing related objects. + * Defaults to null, meaning using zero-based integer IDs. + * @since 1.0.7 + */ + public $index; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array the dynamically specified criteria + * @since 1.0.5 + */ + public function mergeWith($criteria) + { + parent::mergeWith($criteria); + if(isset($criteria['limit']) && $criteria['limit']>0) + $this->limit=$criteria['limit']; + + if(isset($criteria['offset']) && $criteria['offset']>=0) + $this->offset=$criteria['offset']; + + if(isset($criteria['index'])) + $this->index=$criteria['index']; + } +} + + +/** + * CManyManyRelation represents the parameters specifying a MANY_MANY relation. + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CManyManyRelation extends CHasManyRelation +{ +} + + +/** + * CActiveRecordMetaData represents the meta-data for an Active Record class. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecord.php 1305 2009-08-07 16:02:29Z qiang.xue $ + * @package system.db.ar + * @since 1.0 + */ +class CActiveRecordMetaData +{ + /** + * @var CDbTableSchema the table schema information + */ + public $tableSchema; + /** + * @var array table columns + */ + public $columns; + /** + * @var array list of relations + */ + public $relations=array(); + /** + * @var array attribute default values + */ + public $attributeDefaults=array(); + + private $_model; + private $_validators; + + /** + * Constructor. + * @param CActiveRecord the model instance + */ + public function __construct($model) + { + $this->_model=$model; + + $tableName=$model->tableName(); + if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null) + throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.', + array('{class}'=>get_class($model),'{table}'=>$tableName))); + if($table->primaryKey===null) + $table->primaryKey=$model->primaryKey(); + $this->tableSchema=$table; + $this->columns=$table->columns; + + foreach($table->columns as $name=>$column) + { + if(!$column->isPrimaryKey && $column->defaultValue!==null) + $this->attributeDefaults[$name]=$column->defaultValue; + } + + foreach($model->relations() as $name=>$config) + { + if(isset($config[0],$config[1],$config[2])) // relation class, AR class, FK + $this->relations[$name]=new $config[0]($name,$config[1],$config[2],array_slice($config,3)); + else + throw new CDbException(Yii::t('yii','Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.', + array('{class}'=>get_class($model),'{relation}'=>$name))); + } + } + + /** + * @return array list of validators + */ + public function getValidators() + { + if(!$this->_validators) + $this->_validators=$this->_model->createValidators(); + return $this->_validators; + } +} diff --git a/web/framework-1.0.8/db/ar/CActiveRecordBehavior.php b/web/framework-1.0.8/db/ar/CActiveRecordBehavior.php new file mode 100644 index 0000000..09dbaa1 --- /dev/null +++ b/web/framework-1.0.8/db/ar/CActiveRecordBehavior.php @@ -0,0 +1,96 @@ +<?php +/** + * CActiveRecordBehavior class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CActiveRecordBehavior is the base class for behaviors that can be attached to {@link CActiveRecord}. + * Compared with {@link CModelBehavior}, CActiveRecordBehavior attaches to more events + * that are only defined by {@link CActiveRecord}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CActiveRecordBehavior.php 1185 2009-06-28 13:06:00Z qiang.xue $ + * @package system.db.ar + * @since 1.0.2 + */ +class CActiveRecordBehavior extends CModelBehavior +{ + /** + * Declares events and the corresponding event handler methods. + * If you override this method, make sure you merge the parent result to the return value. + * @return array events (array keys) and the corresponding event handler methods (array values). + * @see CBehavior::events + */ + public function events() + { + return array_merge(parent::events(), array( + 'onBeforeSave'=>'beforeSave', + 'onAfterSave'=>'afterSave', + 'onBeforeDelete'=>'beforeDelete', + 'onAfterDelete'=>'afterDelete', + 'onAfterConstruct'=>'afterConstruct', + 'onAfterFind'=>'afterFind', + )); + } + + /** + * Responds to {@link CActiveRecord::onBeforeSave} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * You may set {@link CModelEvent::isValid} to be false to quit the saving process. + * @param CModelEvent event parameter + */ + public function beforeSave($event) + { + } + + /** + * Responds to {@link CActiveRecord::onAfterSave} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * @param CModelEvent event parameter + */ + public function afterSave($event) + { + } + + /** + * Responds to {@link CActiveRecord::onBeforeDelete} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * You may set {@link CModelEvent::isValid} to be false to quit the deletion process. + * @param CEvent event parameter + */ + public function beforeDelete($event) + { + } + + /** + * Responds to {@link CActiveRecord::onAfterDelete} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * @param CEvent event parameter + */ + public function afterDelete($event) + { + } + + /** + * Responds to {@link CActiveRecord::onAfterConstruct} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * @param CEvent event parameter + */ + public function afterConstruct($event) + { + } + + /** + * Responds to {@link CActiveRecord::onAfterFind} event. + * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}. + * @param CEvent event parameter + */ + public function afterFind($event) + { + } +} diff --git a/web/framework-1.0.8/db/schema/CDbColumnSchema.php b/web/framework-1.0.8/db/schema/CDbColumnSchema.php new file mode 100644 index 0000000..19a359a --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbColumnSchema.php @@ -0,0 +1,143 @@ +<?php +/** + * CDbColumnSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbColumnSchema class describes the column meta data of a database table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbColumnSchema.php 538 2009-01-15 19:34:03Z qiang.xue $ + * @package system.db.schema + * @since 1.0 + */ +class CDbColumnSchema extends CComponent +{ + /** + * @var string name of this column (without quotes). + */ + public $name; + /** + * @var string raw name of this column. This is the quoted name that can be used in SQL queries. + */ + public $rawName; + /** + * @var boolean whether this column can be null. + */ + public $allowNull; + /** + * @var string the DB type of this column. + */ + public $dbType; + /** + * @var string the PHP type of this column. + */ + public $type; + /** + * @var mixed default value of this column + */ + public $defaultValue; + /** + * @var integer size of the column. + */ + public $size; + /** + * @var integer precision of the column data, if it is numeric. + */ + public $precision; + /** + * @var integer scale of the column data, if it is numeric. + */ + public $scale; + /** + * @var boolean whether this column is a primary key + */ + public $isPrimaryKey; + /** + * @var boolean whether this column is a foreign key + */ + public $isForeignKey; + + + /** + * Initializes the column with its DB type and default value. + * This sets up the column's PHP type, size, precision, scale as well as default value. + * @param string the column's DB type + * @param mixed the default value + */ + public function init($dbType, $defaultValue) + { + $this->dbType=$dbType; + $this->extractType($dbType); + $this->extractLimit($dbType); + if($defaultValue!==null) + $this->extractDefault($defaultValue); + } + + /** + * Extracts the PHP type from DB type. + * @param string DB type + */ + protected function extractType($dbType) + { + if(stripos($dbType,'int')!==false) + $this->type='integer'; + else if(stripos($dbType,'bool')!==false) + $this->type='boolean'; + else if(preg_match('/(real|floa|doub)/i',$dbType)) + $this->type='double'; + else + $this->type='string'; + } + + /** + * Extracts size, precision and scale information from column's DB type. + * @param string the column's DB type + */ + protected function extractLimit($dbType) + { + if(strpos($dbType,'(') && preg_match('/\((.*)\)/',$dbType,$matches)) + { + $values=explode(',',$matches[1]); + $this->size=$this->precision=(int)$values[0]; + if(isset($values[1])) + $this->scale=(int)$values[1]; + } + } + + /** + * Extracts the default value for the column. + * The value is typecasted to correct PHP type. + * @param mixed the default value obtained from metadata + */ + protected function extractDefault($defaultValue) + { + $this->defaultValue=$this->typecast($defaultValue); + } + + /** + * Converts the input value to the type that this column is of. + * @param mixed input value + * @return mixed converted value + */ + public function typecast($value) + { + if(gettype($value)===$this->type || $value===null || $value instanceof CDbExpression) + return $value; + if($value==='') + return $this->type==='string' ? '' : null; + switch($this->type) + { + case 'integer': return (integer)$value; + case 'boolean': return (boolean)$value; + case 'double': return (double)$value; + case 'string': return (string)$value; + default: return $value; + } + } +} diff --git a/web/framework-1.0.8/db/schema/CDbCommandBuilder.php b/web/framework-1.0.8/db/schema/CDbCommandBuilder.php new file mode 100644 index 0000000..814ca80 --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbCommandBuilder.php @@ -0,0 +1,664 @@ +<?php +/** + * CDbCommandBuilder class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCommandBuilder provides basic methods to create query commands for tables. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbCommandBuilder.php 1302 2009-08-07 13:43:18Z qiang.xue $ + * @package system.db.schema + * @since 1.0 + */ +class CDbCommandBuilder extends CComponent +{ + const PARAM_PREFIX=':yp'; + + private $_schema; + private $_connection; + + /** + * @param CDbSchema the schema for this command builder + */ + public function __construct($schema) + { + $this->_schema=$schema; + $this->_connection=$schema->getDbConnection(); + } + + /** + * @return CDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return CDbSchema the schema for this command builder. + */ + public function getSchema() + { + return $this->_schema; + } + + /** + * Returns the last insertion ID for the specified table. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @return mixed last insertion id. Null is returned if no sequence name. + */ + public function getLastInsertID($table) + { + $this->ensureTable($table); + if($table->sequenceName!==null) + return $this->_connection->getLastInsertID($table->sequenceName); + else + return null; + } + + /** + * Creates a SELECT command for a single table. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param CDbCriteria the query criteria + * @return CDbCommand query command. + */ + public function createFindCommand($table,$criteria) + { + $this->ensureTable($table); + $select=is_array($criteria->select) ? implode(', ',$criteria->select) : $criteria->select; + $sql="SELECT {$select} FROM {$table->rawName}"; + $sql=$this->applyJoin($sql,$criteria->join); + $sql=$this->applyCondition($sql,$criteria->condition); + $sql=$this->applyGroup($sql,$criteria->group); + $sql=$this->applyHaving($sql,$criteria->having); + $sql=$this->applyOrder($sql,$criteria->order); + $sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset); + $command=$this->_connection->createCommand($sql); + $this->bindValues($command,$criteria->params); + return $command; + } + + /** + * Creates a COUNT(*) command for a single table. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param CDbCriteria the query criteria + * @return CDbCommand query command. + */ + public function createCountCommand($table,$criteria) + { + $this->ensureTable($table); + $criteria->select='COUNT(*)'; + return $this->createFindCommand($table,$criteria); + } + + /** + * Creates a DELETE command. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param CDbCriteria the query criteria + * @return CDbCommand delete command. + */ + public function createDeleteCommand($table,$criteria) + { + $this->ensureTable($table); + $sql="DELETE FROM {$table->rawName}"; + $sql=$this->applyJoin($sql,$criteria->join); + $sql=$this->applyCondition($sql,$criteria->condition); + $sql=$this->applyGroup($sql,$criteria->group); + $sql=$this->applyHaving($sql,$criteria->having); + $sql=$this->applyOrder($sql,$criteria->order); + $sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset); + $command=$this->_connection->createCommand($sql); + $this->bindValues($command,$criteria->params); + return $command; + } + + /** + * Creates an INSERT command. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array data to be inserted (column name=>column value). If a key is not a valid column name, the corresponding value will be ignored. + * @return CDbCommand insert command + */ + public function createInsertCommand($table,$data) + { + $this->ensureTable($table); + $fields=array(); + $values=array(); + $placeholders=array(); + $i=0; + foreach($data as $name=>$value) + { + if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull)) + { + $fields[]=$column->rawName; + if($value instanceof CDbExpression) + $placeholders[]=$value->expression; + else + { + $placeholders[]=self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; + } + } + } + if($fields===array()) + { + $pks=is_array($table->primaryKey) ? $table->primaryKey : array($table->primaryKey); + foreach($pks as $pk) + { + $fields[]=$table->getColumn($pk)->rawName; + $placeholders[]='NULL'; + } + } + $sql="INSERT INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).')'; + $command=$this->_connection->createCommand($sql); + + foreach($values as $name=>$value) + $command->bindValue($name,$value); + + return $command; + } + + /** + * Creates an UPDATE command. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array list of columns to be updated (name=>value) + * @param CDbCriteria the query criteria + * @return CDbCommand update command. + */ + public function createUpdateCommand($table,$data,$criteria) + { + $this->ensureTable($table); + $fields=array(); + $values=array(); + $bindByPosition=isset($criteria->params[0]); + $i=0; + foreach($data as $name=>$value) + { + if(($column=$table->getColumn($name))!==null) + { + if($value instanceof CDbExpression) + $fields[]=$column->rawName.'='.$value->expression; + else if($bindByPosition) + { + $fields[]=$column->rawName.'=?'; + $values[]=$column->typecast($value); + } + else + { + $fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; + } + } + } + if($fields===array()) + throw new CDbException(Yii::t('yii','No columns are being updated for table "{table}".', + array('{table}'=>$table->name))); + $sql="UPDATE {$table->rawName} SET ".implode(', ',$fields); + $sql=$this->applyJoin($sql,$criteria->join); + $sql=$this->applyCondition($sql,$criteria->condition); + $sql=$this->applyOrder($sql,$criteria->order); + $sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset); + + $command=$this->_connection->createCommand($sql); + $this->bindValues($command,array_merge($values,$criteria->params)); + + return $command; + } + + /** + * Creates an UPDATE command that increments/decrements certain columns. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param CDbCriteria the query criteria + * @param array counters to be updated (counter increments/decrements indexed by column names.) + * @return CDbCommand the created command + * @throws CException if no counter is specified + */ + public function createUpdateCounterCommand($table,$counters,$criteria) + { + $this->ensureTable($table); + $fields=array(); + foreach($counters as $name=>$value) + { + if(($column=$table->getColumn($name))!==null) + { + $value=(int)$value; + if($value<0) + $fields[]="{$column->rawName}={$column->rawName}-".(-$value); + else + $fields[]="{$column->rawName}={$column->rawName}+".$value; + } + } + if($fields!==array()) + { + $sql="UPDATE {$table->rawName} SET ".implode(', ',$fields); + $sql=$this->applyJoin($sql,$criteria->join); + $sql=$this->applyCondition($sql,$criteria->condition); + $sql=$this->applyOrder($sql,$criteria->order); + $sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset); + $command=$this->_connection->createCommand($sql); + $this->bindValues($command,$criteria->params); + return $command; + } + else + throw new CDbException(Yii::t('yii','No counter columns are being updated for table "{table}".', + array('{table}'=>$table->name))); + } + + /** + * Creates a command based on a given SQL statement. + * @param string the explicitly specified SQL statement + * @param array parameters that will be bound to the SQL statement + * @return CDbCommand the created command + */ + public function createSqlCommand($sql,$params=array()) + { + $command=$this->_connection->createCommand($sql); + $this->bindValues($command,$params); + return $command; + } + + /** + * Alters the SQL to apply JOIN clause. + * @param string the SQL statement to be altered + * @param string the JOIN clause (starting with join type, such as INNER JOIN) + * @return string the altered SQL statement + */ + public function applyJoin($sql,$join) + { + if($join!=='') + return $sql.' '.$join; + else + return $sql; + } + + /** + * Alters the SQL to apply WHERE clause. + * @param string the SQL statement without WHERE clause + * @param string the WHERE clause (without WHERE keyword) + * @return string the altered SQL statement + */ + public function applyCondition($sql,$condition) + { + if($condition!=='') + return $sql.' WHERE '.$condition; + else + return $sql; + } + + /** + * Alters the SQL to apply ORDER BY. + * @param string SQL statement without ORDER BY. + * @param string column ordering + * @return string modified SQL applied with ORDER BY. + */ + public function applyOrder($sql,$orderBy) + { + if($orderBy!=='') + return $sql.' ORDER BY '.$orderBy; + else + return $sql; + } + + /** + * Alters the SQL to apply LIMIT and OFFSET. + * Default implementation is applicable for PostgreSQL, MySQL and SQLite. + * @param string SQL query string without LIMIT and OFFSET. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with LIMIT and OFFSET + */ + public function applyLimit($sql,$limit,$offset) + { + if($limit>=0) + $sql.=' LIMIT '.(int)$limit; + if($offset>0) + $sql.=' OFFSET '.(int)$offset; + return $sql; + } + + /** + * Alters the SQL to apply GROUP BY. + * @param string SQL query string without GROUP BY. + * @param string GROUP BY + * @return string SQL with GROUP BY. + */ + public function applyGroup($sql,$group) + { + if($group!=='') + return $sql.' GROUP BY '.$group; + else + return $sql; + } + + /** + * Alters the SQL to apply HAVING. + * @param string SQL query string without HAVING + * @param string HAVING + * @return string SQL with HAVING + * @since 1.0.1 + */ + public function applyHaving($sql,$having) + { + if($having!=='') + return $sql.' HAVING '.$having; + else + return $sql; + } + + /** + * Binds parameter values for an SQL command. + * @param CDbCommand database command + * @param array values for binding (integer-indexed array for question mark placeholders, string-indexed array for named placeholders) + */ + public function bindValues($command, $values) + { + if(($n=count($values))===0) + return; + if(isset($values[0])) // question mark placeholders + { + for($i=0;$i<$n;++$i) + $command->bindValue($i+1,$values[$i]); + } + else // named placeholders + { + foreach($values as $name=>$value) + { + if($name[0]!==':') + $name=':'.$name; + $command->bindValue($name,$value); + } + } + } + + /** + * Creates a query criteria. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param mixed query condition or criteria. + * If a string, it is treated as query condition (the WHERE clause); + * If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object; + * Otherwise, it should be an instance of {@link CDbCriteria}. + * @param array parameters to be bound to an SQL statement. + * This is only used when the first parameter is a string (query condition). + * In other cases, please use {@link CDbCriteria::params} to set parameters. + * @return CDbCriteria the created query criteria + * @throws CException if the condition is not string, array and CDbCriteria + */ + public function createCriteria($condition='',$params=array()) + { + if(is_array($condition)) + $criteria=new CDbCriteria($condition); + else if($condition instanceof CDbCriteria) + $criteria=clone $condition; + else + { + $criteria=new CDbCriteria; + $criteria->condition=$condition; + $criteria->params=$params; + } + return $criteria; + } + + /** + * Creates a query criteria with the specified primary key. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param mixed primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed query condition or criteria. + * If a string, it is treated as query condition; + * If an array, it is treated as the initial values for constructing a {@link CDbCriteria}; + * Otherwise, it should be an instance of {@link CDbCriteria}. + * @param array parameters to be bound to an SQL statement. + * This is only used when the second parameter is a string (query condition). + * In other cases, please use {@link CDbCriteria::params} to set parameters. + * @return CDbCriteria the created query criteria + */ + public function createPkCriteria($table,$pk,$condition='',$params=array()) + { + $this->ensureTable($table); + $criteria=$this->createCriteria($condition,$params); + if(!is_array($pk)) // single key + $pk=array($pk); + if(is_array($table->primaryKey) && !isset($pk[0]) && $pk!==array()) // single composite key + $pk=array($pk); + $condition=$this->createInCondition($table,$table->primaryKey,$pk); + if($criteria->condition!=='') + $criteria->condition=$condition.' AND ('.$criteria->condition.')'; + else + $criteria->condition=$condition; + + return $criteria; + } + + /** + * Generates the expression for selecting rows of specified primary key values. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array list of primary key values to be selected within + * @param string column prefix (ended with dot). If null, it will be the table name + * @return string the expression for selection + */ + public function createPkCondition($table,$values,$prefix=null) + { + $this->ensureTable($table); + return $this->createInCondition($table,$table->primaryKey,$values,$prefix); + } + + /** + * Creates a query criteria with the specified column values. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array column values that should be matched in the query (name=>value) + * @param mixed query condition or criteria. + * If a string, it is treated as query condition; + * If an array, it is treated as the initial values for constructing a {@link CDbCriteria}; + * Otherwise, it should be an instance of {@link CDbCriteria}. + * @param array parameters to be bound to an SQL statement. + * This is only used when the second parameter is a string (query condition). + * In other cases, please use {@link CDbCriteria::params} to set parameters. + * @return CDbCriteria the created query criteria + */ + public function createColumnCriteria($table,$columns,$condition='',$params=array()) + { + $this->ensureTable($table); + $criteria=$this->createCriteria($condition,$params); + $bindByPosition=isset($criteria->params[0]); + $conditions=array(); + $values=array(); + $i=0; + foreach($columns as $name=>$value) + { + if(($column=$table->getColumn($name))!==null) + { + if(is_array($value)) + $conditions[]=$this->createInCondition($table,$name,$value); + else if($value!==null) + { + if($bindByPosition) + { + $conditions[]=$table->rawName.'.'.$column->rawName.'=?'; + $values[]=$value; + } + else + { + $conditions[]=$table->rawName.'.'.$column->rawName.'='.self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$value; + $i++; + } + } + else + $conditions[]=$table->rawName.'.'.$column->rawName.' IS NULL'; + } + else + throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".', + array('{table}'=>$table->name,'{column}'=>$name))); + } + $criteria->params=array_merge($values,$criteria->params); + if(isset($conditions[0])) + { + if($criteria->condition!=='') + $criteria->condition=implode(' AND ',$conditions).' AND ('.$criteria->condition.')'; + else + $criteria->condition=implode(' AND ',$conditions); + } + return $criteria; + } + + /** + * Generates the expression for searching the specified keywords within a list of columns. + * The search expression is generated using the 'LIKE' SQL syntax. + * Every word in the keywords must be present and appear in at least one of the columns. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array list of column names for potential search condition. + * @param mixed search keywords. This can be either a string with space-separated keywords or an array of keywords. + * @param string optional column prefix (with dot at the end). If null, the table name will be used as the prefix. + * @param boolean whether the search is case-sensitive. Defaults to true. This parameter + * has been available since version 1.0.4. + * @return string SQL search condition matching on a set of columns. An empty string is returned + * if either the column array or the keywords are empty. + */ + public function createSearchCondition($table,$columns,$keywords,$prefix=null,$caseSensitive=true) + { + $this->ensureTable($table); + if(!is_array($keywords)) + $keywords=preg_split('/\s+/u',$keywords,-1,PREG_SPLIT_NO_EMPTY); + if(empty($keywords)) + return ''; + if($prefix===null) + $prefix=$table->rawName.'.'; + $conditions=array(); + foreach($columns as $name) + { + if(($column=$table->getColumn($name))===null) + throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".', + array('{table}'=>$table->name,'{column}'=>$name))); + $condition=array(); + foreach($keywords as $keyword) + { + if($caseSensitive) + $condition[]=$prefix.$column->rawName.' LIKE '.$this->_connection->quoteValue('%'.$keyword.'%'); + else + $condition[]='LOWER('.$prefix.$column->rawName.') LIKE LOWER('.$this->_connection->quoteValue('%'.$keyword.'%').')'; + } + $conditions[]=implode(' AND ',$condition); + } + return '('.implode(' OR ',$conditions).')'; + } + + /** + * Generates the expression for selecting rows of specified primary key values. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param mixed the column name(s). It can be either a string indicating a single column + * or an array of column names. If the latter, it stands for a composite key. + * @param array list of key values to be selected within + * @param string column prefix (ended with dot). If null, it will be the table name + * @return string the expression for selection + * @since 1.0.4 + */ + public function createInCondition($table,$columnName,$values,$prefix=null) + { + if(($n=count($values))<1) + return '0=1'; + + $this->ensureTable($table); + + if($prefix===null) + $prefix=$table->rawName.'.'; + + $db=$this->_connection; + + if(is_array($columnName) && count($columnName)===1) + $columnName=reset($columnName); + + if(is_string($columnName)) // simple key + { + if(!isset($table->columns[$columnName])) + throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".', + array('{table}'=>$table->name, '{column}'=>$columnName))); + $column=$table->columns[$columnName]; + + foreach($values as &$value) + { + $value=$column->typecast($value); + if(is_string($value)) + $value=$db->quoteValue($value); + } + if($n===1) + return $prefix.$column->rawName.($values[0]===null?' IS NULL':'='.$values[0]); + else + return $prefix.$column->rawName.' IN ('.implode(', ',$values).')'; + } + else if(is_array($columnName)) // composite key: $values=array(array('pk1'=>'v1','pk2'=>'v2'),array(...)) + { + foreach($columnName as $name) + { + if(!isset($table->columns[$name])) + throw new CDbException(Yii::t('yii','Table "{table}" does not have a column named "{column}".', + array('{table}'=>$table->name, '{column}'=>$name))); + + for($i=0;$i<$n;++$i) + { + if(isset($values[$i][$name])) + { + $value=$table->columns[$name]->typecast($values[$i][$name]); + if(is_string($value)) + $values[$i][$name]=$db->quoteValue($value); + else + $values[$i][$name]=$value; + } + else + throw new CDbException(Yii::t('yii','The value for the column "{column}" is not supplied when querying the table "{table}".', + array('{table}'=>$table->name,'{column}'=>$name))); + } + } + if(count($values)===1) + { + $entries=array(); + foreach($values[0] as $name=>$value) + $entries[]=$prefix.$table->columns[$name]->rawName.($value===null?' IS NULL':'='.$value); + return implode(' AND ',$entries); + } + + return $this->createCompositeInCondition($table,$values,$prefix); + } + else + throw new CDbException(Yii::t('yii','Column name must be either a string or an array.')); + } + + /** + * Generates the expression for selecting rows with specified composite key values. + * @param CDbTableSchema the table schema + * @param array list of primary key values to be selected within + * @param string column prefix (ended with dot) + * @return string the expression for selection + * @since 1.0.4 + */ + protected function createCompositeInCondition($table,$values,$prefix) + { + $keyNames=array(); + foreach(array_keys($values[0]) as $name) + $keyNames[]=$prefix.$table->columns[$name]->rawName; + $vs=array(); + foreach($values as $value) + $vs[]='('.implode(', ',$value).')'; + return '('.implode(', ',$keyNames).') IN ('.implode(', ',$vs).')'; + } + + /** + * Checks if the parameter is a valid table schema. + * If it is a string, the corresponding table schema will be retrieved. + * @param mixed table schema ({@link CDbTableSchema}) or table name (string). + * If this refers to a valid table name, this parameter will be returned with the corresponding table schema. + * @throws CDbException if the table name is not valid + * @since 1.0.4 + */ + protected function ensureTable(&$table) + { + if(is_string($table) && ($table=$this->_schema->getTable($tableName=$table))===null) + throw new CDbException(Yii::t('yii','Table "{table}" does not exist.', + array('{table}'=>$tableName))); + } +} diff --git a/web/framework-1.0.8/db/schema/CDbCriteria.php b/web/framework-1.0.8/db/schema/CDbCriteria.php new file mode 100644 index 0000000..bfcdcd2 --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbCriteria.php @@ -0,0 +1,166 @@ +<?php +/** + * CDbCriteria class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbCriteria represents a query criteria, such as conditions, ordering by, limit/offset. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbCriteria.php 1041 2009-05-21 18:01:31Z qiang.xue $ + * @package system.db.schema + * @since 1.0 + */ +class CDbCriteria +{ + /** + * @var mixed the columns being selected. This refers to the SELECT clause in an SQL + * statement. The property can be either a string (column names separated by commas) + * or an array of column names. Defaults to '*', meaning all columns. + */ + public $select='*'; + /** + * @var string query condition. This refers to the WHERE clause in an SQL statement. + * For example, <code>age>31 AND team=1</code>. + */ + public $condition=''; + /** + * @var array list of query parameter values indexed by parameter placeholders. + * For example, <code>array(':name'=>'Dan', ':age'=>31)</code>. + */ + public $params=array(); + /** + * @var integer maximum number of records to be returned. If less than 0, it means no limit. + */ + public $limit=-1; + /** + * @var integer zero-based offset from where the records are to be returned. If less than 0, it means starting from the beginning. + */ + public $offset=-1; + /** + * @var string how to sort the query results. This refers to the ORDER BY clause in an SQL statement. + */ + public $order=''; + /** + * @var string how to group the query results. This refers to the GROUP BY clause in an SQL statement. + * For example, <code>'projectID, teamID'</code>. + */ + public $group=''; + /** + * @var string how to join with other tables. This refers to the JOIN clause in an SQL statement. + * For example, <code>'LEFT JOIN users ON users.id=authorID'</code>. + */ + public $join=''; + /** + * @var string the condition to be applied with GROUP-BY clause. + * For example, <code>'SUM(revenue)<50000'</code>. + * @since 1.0.1 + */ + public $having=''; + + /** + * Constructor. + * @param array criteria initial property values (indexed by property name) + */ + public function __construct($data=array()) + { + foreach($data as $name=>$value) + $this->$name=$value; + } + + /** + * Merges with another criteria. + * In general, the merging makes the resulting criteria more restrictive. + * For example, if both criterias have conditions, they will be 'AND' together. + * Also, the criteria passed as the parameter takes precedence in case + * two options cannot be merged (e.g. LIMIT, OFFSET). + * @param CDbCriteria the criteria to be merged with. + * @param boolean whether to use 'AND' to merge condition and having options. + * If false, 'OR' will be used instead. Defaults to 'AND'. This parameter has been + * available since version 1.0.6. + * @since 1.0.5 + */ + public function mergeWith($criteria,$useAnd=true) + { + $and=$useAnd ? 'AND' : 'OR'; + if(is_array($criteria)) + $criteria=new self($criteria); + if($this->select!==$criteria->select) + { + if($this->select==='*') + $this->select=$criteria->select; + else if($criteria->select!=='*') + { + $select1=is_string($this->select)?preg_split('/\s*,\s*/',trim($this->select),-1,PREG_SPLIT_NO_EMPTY):$this->select; + $select2=is_string($criteria->select)?preg_split('/\s*,\s*/',trim($criteria->select),-1,PREG_SPLIT_NO_EMPTY):$criteria->select; + $this->select=array_merge($select1,array_diff($select2,$select1)); + } + } + + if($this->condition!==$criteria->condition) + { + if($this->condition==='') + $this->condition=$criteria->condition; + else if($criteria->condition!=='') + $this->condition="({$this->condition}) $and ({$criteria->condition})"; + } + + if($this->params!==$criteria->params) + $this->params=array_merge($this->params,$criteria->params); + + if($criteria->limit>0) + $this->limit=$criteria->limit; + + if($criteria->offset>=0) + $this->offset=$criteria->offset; + + if($this->order!==$criteria->order) + { + if($this->order==='') + $this->order=$criteria->order; + else if($criteria->order!=='') + $this->order.=', '.$criteria->order; + } + + if($this->group!==$criteria->group) + { + if($this->group==='') + $this->group=$criteria->group; + else if($criteria->group!=='') + $this->group.=', '.$criteria->group; + } + + if($this->join!==$criteria->join) + { + if($this->join==='') + $this->join=$criteria->join; + else if($criteria->join!=='') + $this->join.=' '.$criteria->join; + } + + if($this->having!==$criteria->having) + { + if($this->having==='') + $this->having=$criteria->having; + else if($criteria->having!=='') + $this->having="({$this->having}) $and ({$criteria->having})"; + } + } + + /** + * @return array the array representation of the criteria + * @since 1.0.6 + */ + public function toArray() + { + $result=array(); + foreach(array('select', 'condition', 'params', 'limit', 'offset', 'order', 'group', 'join', 'having') as $name) + $result[$name]=$this->$name; + return $result; + } +} diff --git a/web/framework-1.0.8/db/schema/CDbExpression.php b/web/framework-1.0.8/db/schema/CDbExpression.php new file mode 100644 index 0000000..9d52b17 --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbExpression.php @@ -0,0 +1,49 @@ +<?php +/** + * CDbExpression class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbExpression represents a DB expression that does not need escaping. + * CDbExpression is mainly used in {@link CActiveRecord} as attribute values. + * When inserting or updating a {@link CActiveRecord}, attribute values of + * type CDbExpression will be directly put into the corresponding SQL statement + * without escaping. A typical usage is that an attribute is set with 'NOW()' + * expression so that saving the record would fill the corresponding column + * with the current DB server timestamp. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbExpression.php 544 2009-01-18 13:27:31Z qiang.xue $ + * @package system.db.schema + * @since 1.0.2 + */ +class CDbExpression extends CComponent +{ + /** + * @var string the DB expression + */ + public $expression; + + /** + * Constructor. + * @param string the DB expression + */ + public function __construct($expression) + { + $this->expression=$expression; + } + + /** + * String magic method + * @return string the DB expression + */ + public function __toString() + { + return $this->expression; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/db/schema/CDbSchema.php b/web/framework-1.0.8/db/schema/CDbSchema.php new file mode 100644 index 0000000..523f501 --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbSchema.php @@ -0,0 +1,203 @@ +<?php +/** + * CDbSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbSchema is the base class for retrieving metadata information. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbSchema.php 1304 2009-08-07 15:02:50Z qiang.xue $ + * @package system.db.schema + * @since 1.0 + */ +abstract class CDbSchema extends CComponent +{ + private $_tableNames=array(); + private $_tables=array(); + private $_connection; + private $_builder; + private $_cacheExclude=array(); + + /** + * Creates a table instance representing the metadata for the named table. + * @return CDbTableSchema driver dependent table metadata, null if the table does not exist. + */ + abstract protected function createTable($name); + + /** + * Constructor. + * @param CDbConnection database connection. + */ + public function __construct($conn) + { + $conn->setActive(true); + $this->_connection=$conn; + foreach($conn->schemaCachingExclude as $name) + $this->_cacheExclude[$name]=true; + } + + /** + * @return CDbConnection database connection. The connection is active. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * Obtains the metadata for the named table. + * @param string table name + * @return CDbTableSchema table metadata. Null if the named table does not exist. + */ + public function getTable($name) + { + if(isset($this->_tables[$name])) + return $this->_tables[$name]; + else if(!isset($this->_cacheExclude[$name]) && ($duration=$this->_connection->schemaCachingDuration)>0 && ($cache=Yii::app()->getCache())!==null) + { + $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name; + if(($table=$cache->get($key))===false) + { + $table=$this->createTable($name); + $cache->set($key,$table,$duration); + } + return $this->_tables[$name]=$table; + } + else + return $this->_tables[$name]=$this->createTable($name); + } + + /** + * Returns the metadata for all tables in the database. + * @param string the schema of the tables. Defaults to empty string, meaning the current or default schema. + * @return array the metadata for all tables in the database. + * Each array element is an instance of {@link CDbTableSchema} (or its child class). + * The array keys are table names. + * @since 1.0.2 + */ + public function getTables($schema='') + { + $tables=array(); + foreach($this->getTableNames($schema) as $name) + $tables[$name]=$this->getTable($name); + return $tables; + } + + /** + * Returns all table names in the database. + * @param string the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + * @since 1.0.2 + */ + public function getTableNames($schema='') + { + if(!isset($this->_tableNames[$schema])) + $this->_tableNames[$schema]=$this->findTableNames($schema); + return $this->_tableNames[$schema]; + } + + /** + * @return CDbCommandBuilder the SQL command builder for this connection. + */ + public function getCommandBuilder() + { + if($this->_builder!==null) + return $this->_builder; + else + return $this->_builder=$this->createCommandBuilder(); + } + + /** + * Refreshes the schema. + * This method resets the loaded table metadata and command builder + * so that they can be recreated to reflect the change of schema. + */ + public function refresh() + { + if(($duration=$this->_connection->schemaCachingDuration)>0 && ($cache=Yii::app()->getCache())!==null) + { + foreach(array_keys($this->_tables) as $name) + { + if(!isset($this->_cacheExclude[$name])) + { + $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name; + $cache->delete($key); + } + } + } + $this->_tables=array(); + $this->_tableNames=array(); + $this->_builder=null; + } + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return "'".$name."'"; + } + + /** + * Quotes a column name for use in a query. + * @param string column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return '"'.$name.'"'; + } + + /** + * Compares two table names. + * The table names can be either quoted or unquoted. This method + * will consider both cases. + * @param string table name 1 + * @param string table name 2 + * @return boolean whether the two table names refer to the same table. + */ + public function compareTableNames($name1,$name2) + { + $name1=str_replace(array('"','`',"'"),'',$name1); + $name2=str_replace(array('"','`',"'"),'',$name2); + if(($pos=strrpos($name1,'.'))!==false) + $name1=substr($name1,$pos+1); + if(($pos=strrpos($name2,'.'))!==false) + $name2=substr($name2,$pos+1); + return $name1===$name2; + } + + /** + * Creates a command builder for the database. + * This method may be overridden by child classes to create a DBMS-specific command builder. + * @return CDbCommandBuilder command builder instance + */ + protected function createCommandBuilder() + { + return new CDbCommandBuilder($this); + } + + /** + * Returns all table names in the database. + * This method should be overridden by child classes in order to support this feature + * because the default implemenation simply throws an exception. + * @param string the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + * @since 1.0.2 + */ + protected function findTableNames($schema='') + { + throw new CDbException(Yii::t('yii','{class} does not support fetching all table names.', + array('{class}'=>get_class($this)))); + } +} diff --git a/web/framework-1.0.8/db/schema/CDbTableSchema.php b/web/framework-1.0.8/db/schema/CDbTableSchema.php new file mode 100644 index 0000000..fd03cd4 --- /dev/null +++ b/web/framework-1.0.8/db/schema/CDbTableSchema.php @@ -0,0 +1,76 @@ +<?php +/** + * CDbTableSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbTableSchema is the base class for representing the metadata of a database table. + * + * It may be extended by different DBMS driver to provide DBMS-specific table metadata. + * + * CDbTableSchema provides the following information about a table: + * <ul> + * <li>{@link name}</li> + * <li>{@link rawName}</li> + * <li>{@link columns}</li> + * <li>{@link primaryKey}</li> + * <li>{@link foreignKeys}</li> + * <li>{@link sequenceName}</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbTableSchema.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db.schema + * @since 1.0 + */ +class CDbTableSchema extends CComponent +{ + /** + * @var string name of this table. + */ + public $name; + /** + * @var string raw name of this table. This is the quoted version of table name with optional schema name. It can be directly used in SQLs. + */ + public $rawName; + /** + * @var string|array primary key name of this table. If composite key, an array of key names is returned. + */ + public $primaryKey; + /** + * @var string sequence name for the primary key. Null if no sequence. + */ + public $sequenceName; + /** + * @var array foreign keys of this table. The array is indexed by column name. Each value is an array of foreign table name and foreign column name. + */ + public $foreignKeys=array(); + /** + * @var array column metadata of this table. Each array element is a CDbColumnSchema object, indexed by column names. + */ + public $columns=array(); + + /** + * Gets the named column metadata. + * This is a convenient method for retrieving a named column even if it does not exist. + * @param string column name + * @return CDbColumnSchema metadata of the named column. Null if the named column does not exist. + */ + public function getColumn($name) + { + return isset($this->columns[$name]) ? $this->columns[$name] : null; + } + + /** + * @return array list of column names + */ + public function getColumnNames() + { + return array_keys($this->columns); + } +} diff --git a/web/framework-1.0.8/db/schema/mssql/CMssqlColumnSchema.php b/web/framework-1.0.8/db/schema/mssql/CMssqlColumnSchema.php new file mode 100644 index 0000000..e655840 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mssql/CMssqlColumnSchema.php @@ -0,0 +1,55 @@ +<?php +/** + * CMssqlColumnSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMssqlColumnSchema class describes the column meta data of a MSSQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @version $Id: CMssqlColumnSchema.php 823 2009-03-12 14:07:43Z qiang.xue $ + * @package system.db.schema.mssql + * @since 1.0.4 + */ +class CMssqlColumnSchema extends CDbColumnSchema +{ + /** + * Extracts the PHP type from DB type. + * @param string DB type + */ + protected function extractType($dbType) + { + if(strpos($dbType,'bigint')!==false || strpos($dbType,'float')!==false || strpos($dbType,'real')!==false) + $this->type='double'; + else if(strpos($dbType,'int')!==false || strpos($dbType,'smallint')!==false || strpos($dbType,'tinyint')) + $this->type='integer'; + else if(strpos($dbType,'bit')!==false) + $this->type='boolean'; + else + $this->type='string'; + } + + protected function extractDefault($defaultValue) + { + if($this->dbType==='timestamp' ) + $this->defaultValue=null; + else + parent::extractDefault(str_replace(array('(',')',"'"), '', $defaultValue)); + } + + /** + * Extracts size, precision and scale information from column's DB type. + * We do nothing here, since sizes and precisions have been computed before. + * @param string the column's DB type + */ + protected function extractLimit($dbType) + { + } +} diff --git a/web/framework-1.0.8/db/schema/mssql/CMssqlCommandBuilder.php b/web/framework-1.0.8/db/schema/mssql/CMssqlCommandBuilder.php new file mode 100644 index 0000000..630b417 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mssql/CMssqlCommandBuilder.php @@ -0,0 +1,304 @@ +<?php +/** + * CMsCommandBuilder class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMssqlCommandBuilder provides basic methods to create query commands for tables for Mssql Servers. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id: CMssqlCommandBuilder.php 1281 2009-07-31 21:40:12Z qiang.xue $ + * @package system.db.schema.mssql + * @since 1.0.4 + */ +class CMssqlCommandBuilder extends CDbCommandBuilder +{ + /** + * Returns the last insertion ID for the specified table. + * Override parent implemantation since PDO mssql driver does not provide this method + * @param CDbTableSchema the table metadata + * @return mixed last insertion id. Null is returned if no sequence name. + */ + public function getLastInsertID($table) + { + if($table->sequenceName!==null) + return $this->getDbConnection()->createCommand('SELECT SCOPE_IDENTITY()')->queryScalar(); + else + return null; + } + + /** + * Creates a COUNT(*) command for a single table. + * Override parent implementation to remove the order clause of criteria if it exists + * @param CDbTableSchema the table metadata + * @param CDbCriteria the query criteria + * @return CDbCommand query command. + */ + public function createCountCommand($table,$criteria) + { + $criteria->order=''; + return parent::createCountCommand($table, $criteria); + } + + /** + * Creates a SELECT command for a single table. + * Override parent implementation to check if an orderby clause if specified when querying with an offset + * @param CDbTableSchema the table metadata + * @param CDbCriteria the query criteria + * @return CDbCommand query command. + */ + public function createFindCommand($table,$criteria) + { + $criteria=$this->checkCriteria($table,$criteria); + return parent::createFindCommand($table,$criteria); + + } + + /** + * Creates an UPDATE command. + * Override parent implementation because mssql don't want to update an identity column + * @param CDbTableSchema the table metadata + * @param array list of columns to be updated (name=>value) + * @param CDbCriteria the query criteria + * @return CDbCommand update command. + */ + public function createUpdateCommand($table,$data,$criteria) + { + $criteria=$this->checkCriteria($table,$criteria); + $fields=array(); + $values=array(); + $bindByPosition=isset($criteria->params[0]); + $i=0; + foreach($data as $name=>$value) + { + if(($column=$table->getColumn($name))!==null) + { + if ($table->sequenceName !== null && $column->isPrimaryKey === true) continue; + if ($column->dbType === 'timestamp') continue; + if($value instanceof CDbExpression) + $fields[]=$column->rawName.'='.$value->expression; + else if($bindByPosition) + { + $fields[]=$column->rawName.'=?'; + $values[]=$column->typecast($value); + } + else + { + $fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; + } + } + } + if($fields===array()) + throw new CDbException(Yii::t('yii','No columns are being updated for table "{table}".', + array('{table}'=>$table->name))); + $sql="UPDATE {$table->rawName} SET ".implode(', ',$fields); + $sql=$this->applyJoin($sql,$criteria->join); + $sql=$this->applyCondition($sql,$criteria->condition); + $sql=$this->applyOrder($sql,$criteria->order); + $sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset); + + $command=$this->getDbConnection()->createCommand($sql); + $this->bindValues($command,array_merge($values,$criteria->params)); + + return $command; + } + + /** + * Creates a DELETE command. + * Override parent implementation to check if an orderby clause if specified when querying with an offset + * @param CDbTableSchema the table metadata + * @param CDbCriteria the query criteria + * @return CDbCommand delete command. + */ + public function createDeleteCommand($table,$criteria) + { + $criteria=$this->checkCriteria($table, $criteria); + return parent::createDeleteCommand($table, $criteria); + } + + /** + * Creates an UPDATE command that increments/decrements certain columns. + * Override parent implementation to check if an orderby clause if specified when querying with an offset + * @param CDbTableSchema the table metadata + * @param CDbCriteria the query criteria + * @param array counters to be updated (counter increments/decrements indexed by column names.) + * @return CDbCommand the created command + * @throws CException if no counter is specified + */ + public function createUpdateCounterCommand($table,$counters,$criteria) + { + $criteria=$this->checkCriteria($table, $criteria); + return parent::createUpdateCounterCommand($table, $counters, $criteria); + } + + /** + * This is a port from Prado Framework. + * + * Overrides parent implementation. Alters the sql to apply $limit and $offset. + * The idea for limit with offset is done by modifying the sql on the fly + * with numerous assumptions on the structure of the sql string. + * The modification is done with reference to the notes from + * http://troels.arvin.dk/db/rdbms/#select-limit-offset + * + * <code> + * SELECT * FROM ( + * SELECT TOP n * FROM ( + * SELECT TOP z columns -- (z=n+skip) + * FROM tablename + * ORDER BY key ASC + * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything) + * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything) + * </code> + * + * <b>Regular expressions are used to alter the SQL query. The resulting SQL query + * may be malformed for complex queries.</b> The following restrictions apply + * + * <ul> + * <li> + * In particular, <b>commas</b> should <b>NOT</b> + * be used as part of the ordering expression or identifier. Commas must only be + * used for separating the ordering clauses. + * </li> + * <li> + * In the ORDER BY clause, the column name should NOT be be qualified + * with a table name or view name. Alias the column names or use column index. + * </li> + * <li> + * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses. + * </li> + * + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ + public function applyLimit($sql, $limit, $offset) + { + $limit = $limit!==null ? intval($limit) : -1; + $offset = $offset!==null ? intval($offset) : -1; + if ($limit > 0 && $offset <= 0) //just limit + $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql); + else if($limit > 0 && $offset > 0) + $sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset); + return $sql; + } + + /** + * Rewrite sql to apply $limit > and $offset > 0 for MSSQL database. + * See http://troels.arvin.dk/db/rdbms/#select-limit-offset + * @param string sql query + * @param integer $limit > 0 + * @param integer $offset > 0 + * @return sql modified sql query applied with limit and offset. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ + protected function rewriteLimitOffsetSql($sql, $limit, $offset) + { + $fetch = $limit+$offset; + $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql); + $ordering = $this->findOrdering($sql); + + $orginalOrdering = $this->joinOrdering($ordering); + $reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering)); + $sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner top table__] {$reverseOrdering}) as [__outer top table__] {$orginalOrdering}"; + return $sql; + } + + /** + * Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx + * + * @param string $sql + * @return array ordering expression as key and ordering direction as value + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ + protected function findOrdering($sql) + { + if(!preg_match('/ORDER BY/i', $sql)) + return array(); + $matches=array(); + $ordering=array(); + preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches); + if(count($matches)>1 && count($matches[2]) > 0) + { + $parts = explode(',', $matches[2][0]); + foreach($parts as $part) + { + $subs=array(); + if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs)) + { + if(count($subs) > 1 && count($subs[2]) > 0) + { + $ordering[$subs[1][0]] = $subs[2][0]; + } + //else what? + } + else + $ordering[trim($part)] = 'ASC'; + } + } + return $ordering; + } + + /** + * @param array ordering obtained from findOrdering() + * @return string concat the orderings + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ + protected function joinOrdering($orders) + { + if(count($orders)>0) + { + $str=array(); + foreach($orders as $column => $direction) + $str[] = $column.' '.$direction; + return 'ORDER BY '.implode(', ', $str); + } + } + + /** + * @param array original ordering + * @return array ordering with reversed direction. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + */ + protected function reverseDirection($orders) + { + foreach($orders as $column => $direction) + $orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC'; + return $orders; + } + + + /** + * Checks if the criteria has an order by clause when using offset/limit. + * Override parent implementation to check if an orderby clause if specified when querying with an offset + * If not, order it by pk. + * @param CMssqlTableSchema table schema + * @param CDbCriteria criteria + * @return CDbCrireria the modified criteria + */ + protected function checkCriteria($table, $criteria) + { + if ($criteria->offset > 0 && $criteria->order==='') + { + $criteria->order=is_array($table->primaryKey)?implode(',',$table->primaryKey):$table->primaryKey; + } + return $criteria; + } +} diff --git a/web/framework-1.0.8/db/schema/mssql/CMssqlPdoAdapter.php b/web/framework-1.0.8/db/schema/mssql/CMssqlPdoAdapter.php new file mode 100644 index 0000000..ff0ba45 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mssql/CMssqlPdoAdapter.php @@ -0,0 +1,74 @@ +<?php +/** + * CMssqlPdo class file + * + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * This is an extension of default PDO class for mssql driver only + * It provides some missing functionalities of pdo driver + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @version $Id: CMssqlPdoAdapter.php 823 2009-03-12 14:07:43Z qiang.xue $ + * @package system.db.schema.mssql + * @since 1.0.4 + */ +class CMssqlPdoAdapter extends PDO +{ + /** + * Get the last inserted id value + * MSSQL doesn't support sequence, so, argument is ignored + * + * @param string sequence name. Defaults to null + * @return int last inserted id + */ + public function lastInsertId ($sequence=NULL) + { + return $this->query('SELECT SCOPE_IDENTITY()')->fetchColumn(); + } + + /** + * Begin a transaction + * + * Is is necessary to override pdo's method, as mssql pdo drivers + * does not support transaction + * + * @return boolean + */ + public function beginTransaction () + { + $this->exec('BEGIN TRANSACTION'); + return true; + } + + /** + * Commit a transaction + * + * Is is necessary to override pdo's method, as mssql pdo drivers + * does not support transaction + * + * @return boolean + */ + public function commit () + { + $this->exec('COMMIT TRANSACTION'); + return true; + } + + /** + * Rollback a transaction + * + * Is is necessary to override pdo's method, ac mssql pdo drivers + * does not support transaction + * + * @return boolean + */ + public function rollBack () + { + $this->exec('ROLLBACK TRANSACTION'); + return true; + } +} diff --git a/web/framework-1.0.8/db/schema/mssql/CMssqlSchema.php b/web/framework-1.0.8/db/schema/mssql/CMssqlSchema.php new file mode 100644 index 0000000..2872f19 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mssql/CMssqlSchema.php @@ -0,0 +1,310 @@ +<?php +/** + * CMssqlSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMssqlSchema is the class for retrieving metadata information from a MS SQL Server database. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @version $Id: CMssqlSchema.php 1197 2009-06-30 23:31:52Z qiang.xue $ + * @package system.db.schema.mssql + * @since 1.0.4 + */ +class CMssqlSchema extends CDbSchema +{ + const DEFAULT_SCHEMA='dbo'; + + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + if (strpos($name,'.')===false) + return '['.$name.']'; + $names=explode('.',$name); + foreach ($names as &$n) + $n = '['.$n.']'; + return implode('.',$names); + } + + /** + * Quotes a column name for use in a query. + * @param string column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return '['.$name.']'; + } + + /** + * Compares two table names. + * The table names can be either quoted or unquoted. This method + * will consider both cases. + * @param string table name 1 + * @param string table name 2 + * @return boolean whether the two table names refer to the same table. + */ + public function compareTableNames($name1,$name2) + { + $name1=str_replace(array('[',']'),'',$name1); + $name1=str_replace(array('[',']'),'',$name2); + return parent::compareTableNames(strtolower($name1),strtolower($name2)); + } + + /** + * Creates a table instance representing the metadata for the named table. + * @return CMssqlTableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function createTable($name) + { + $table=new CMssqlTableSchema; + $this->resolveTableNames($table,$name); + //if (!in_array($table->name, $this->tableNames)) return null; + $table->primaryKey=$this->findPrimaryKey($table); + $table->foreignKeys=$this->findForeignKeys($table); + if($this->findColumns($table)) + { + return $table; + } + else + return null; + } + + /** + * Generates various kinds of table names. + * @param CMssqlTableSchema the table instance + * @param string the unquoted table name + */ + protected function resolveTableNames($table,$name) + { + $parts=explode('.',str_replace(array('[',']'),'',$name)); + if(($c=count($parts))==3) + { + // Catalog name, schema name and table name provided + $table->catalogName=$parts[0]; + $table->schemaName=$parts[1]; + $table->name=$parts[2]; + $table->rawName=$this->quoteTableName($table->catalogName).'.'.$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name); + } + elseif ($c==2) + { + // Only schema name and table name provided + $table->name=$parts[1]; + $table->schemaName=$parts[0]; + $table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name); + } + else + { + // Only the name given, we need to get at least the schema name + //if (empty($this->_schemaNames)) $this->findTableNames(); + $table->name=$parts[0]; + $table->schemaName=self::DEFAULT_SCHEMA; + $table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name); + } + } + + /** + * Gets the primary key column(s) details for the given table. + * @param CMssqlTableSchema table + * @return mixed primary keys (null if no pk, string if only 1 column pk, or array if composite pk) + */ + protected function findPrimaryKey($table) + { + $kcu='INFORMATION_SCHEMA.KEY_COLUMN_USAGE'; + $tc='INFORMATION_SCHEMA.TABLE_CONSTRAINTS'; + if (isset($table->catalogName)) + { + $kcu=$table->catalogName.'.'.$kcu; + $tc=$table->catalogName.'.'.$tc; + } + + $sql = <<<EOD + SELECT k.column_name field_name + FROM {$this->quoteTableName($kcu)} k + LEFT JOIN {$this->quoteTableName($tc)} c + ON k.table_name = c.table_name + AND k.constraint_name = c.constraint_name + WHERE c.constraint_type ='PRIMARY KEY' + AND k.table_name = :table + AND k.table_schema = :schema +EOD; + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $table->name); + $command->bindValue(':schema', $table->schemaName); + $primary=$command->queryColumn(); + switch (count($primary)) + { + case 0: // No primary key on table + $primary=null; + break; + case 1: // Only 1 primary key + $primary=$primary[0]; + break; + } + return $primary; + } + + /** + * Gets foreign relationship constraint keys and table name + * @param CMssqlTableSchema table + * @return array foreign relationship table name and keys. + */ + protected function findForeignKeys($table) + { + $rc='INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS'; + $kcu='INFORMATION_SCHEMA.KEY_COLUMN_USAGE'; + if (isset($table->catalogName)) + { + $kcu=$table->catalogName.'.'.$kcu; + $rc=$table->catalogName.'.'.$rc; + } + + //From http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx + $sql = <<<EOD + SELECT + KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME' + , KCU1.TABLE_NAME AS 'FK_TABLE_NAME' + , KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME' + , KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION' + , KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME' + , KCU2.TABLE_NAME AS 'UQ_TABLE_NAME' + , KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME' + , KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION' + FROM {$this->quoteTableName($rc)} RC + JOIN {$this->quoteTableName($kcu)} KCU1 + ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG + AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA + AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME + JOIN {$this->quoteTableName($kcu)} KCU2 + ON KCU2.CONSTRAINT_CATALOG = + RC.UNIQUE_CONSTRAINT_CATALOG + AND KCU2.CONSTRAINT_SCHEMA = + RC.UNIQUE_CONSTRAINT_SCHEMA + AND KCU2.CONSTRAINT_NAME = + RC.UNIQUE_CONSTRAINT_NAME + AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION + WHERE KCU1.TABLE_NAME = :table +EOD; + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $table->name); + $fkeys=array(); + foreach($command->queryAll() as $info) + { + $fkeys[$info['FK_COLUMN_NAME']]=array($info['UQ_TABLE_NAME'],$info['UQ_COLUMN_NAME'],); + + } + return $fkeys; + } + + + /** + * Collects the table column metadata. + * @param CMssqlTableSchema the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $where=array(); + $where[]="TABLE_NAME='".$table->name."'"; + if (isset($table->catalogName)) + $where[]="TABLE_CATALOG='".$table->catalogName."'"; + if (isset($table->schemaName)) + $where[]="TABLE_SCHEMA='".$table->schemaName."'"; + $sql="SELECT *, columnproperty(object_id(table_schema+'.'+table_name), column_name, 'IsIdentity') as IsIdentity ". + "FROM INFORMATION_SCHEMA.COLUMNS WHERE ".join(' AND ',$where); + if (($columns=$this->getDbConnection()->createCommand($sql)->queryAll())===array()) + return false; + + foreach($columns as $column) + { + $c=$this->createColumn($column); + if (is_array($table->primaryKey)) + $c->isPrimaryKey=in_array($c->name, $table->primaryKey); + else + $c->isPrimaryKey=strcasecmp($c->name,$table->primaryKey)===0; + + $c->isForeignKey=isset($table->foreignKeys[$c->name]); + $table->columns[$c->name]=$c; + if ($column['IsIdentity']==1 && $table->sequenceName===null) + $table->sequenceName=''; + + } + return true; + } + + /** + * Creates a table column. + * @param array column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c=new CMssqlColumnSchema; + $c->name=$column['COLUMN_NAME']; + $c->rawName=$this->quoteColumnName($c->name); + $c->allowNull=$column['IS_NULLABLE']=='YES'; + if ($column['NUMERIC_PRECISION_RADIX']!==null) + { + // We have a numeric datatype + $c->size=$c->precision=$column['NUMERIC_PRECISION']!==null?(int)$column['NUMERIC_PRECISION']:null; + $c->scale=$column['NUMERIC_SCALE']!==null?(int)$column['NUMERIC_SCALE']:null; + } + elseif ($column['DATA_TYPE']=='image' || $column['DATA_TYPE']=='text') + $c->size=$c->precision=null; + else + $c->size=$c->precision=($column['CHARACTER_MAXIMUM_LENGTH']!== null)?(int)$column['CHARACTER_MAXIMUM_LENGTH']:null; + + $c->init($column['DATA_TYPE'],$column['COLUMN_DEFAULT']); + return $c; + } + + /** + * Returns all table names in the database. + * @return array all table names in the database. + * @since 1.0.4 + */ + protected function findTableNames($schema='') + { + if($schema==='') + $schema=self::DEFAULT_SCHEMA; + $sql=<<<EOD +SELECT TABLE_NAME, TABLE_SCHEMA FROM [INFORMATION_SCHEMA].[TABLES] +WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA=:schema +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindParam(":schema", $schema); + $rows=$command->queryAll(); + $names=array(); + foreach ($rows as $row) + { + if ($schema == self::DEFAULT_SCHEMA) + $names[]=$row['TABLE_NAME']; + else + $names[]=$schema.'.'.$row['TABLE_SCHEMA'].'.'.$row['TABLE_NAME']; + } + + return $names; + } + + /** + * Creates a command builder for the database. + * This method overrides parent implementation in order to create a MSSQL specific command builder + * @return CDbCommandBuilder command builder instance + */ + protected function createCommandBuilder() + { + return new CMssqlCommandBuilder($this); + } +} diff --git a/web/framework-1.0.8/db/schema/mssql/CMssqlTableSchema.php b/web/framework-1.0.8/db/schema/mssql/CMssqlTableSchema.php new file mode 100644 index 0000000..265e35b --- /dev/null +++ b/web/framework-1.0.8/db/schema/mssql/CMssqlTableSchema.php @@ -0,0 +1,33 @@ +<?php +/** + * CMssqlTableSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMssqlTableSchema represents the metadata for a MSSQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Christophe Boulain <Christophe.Boulain@gmail.com> + * @version $Id: CMssqlTableSchema.php 820 2009-03-11 17:11:24Z qiang.xue $ + * @package system.db.schema.mssql + * @since 1.0.4 + */ +class CMssqlTableSchema extends CDbTableSchema +{ + /** + * @var string name of the catalog (database) that this table belongs to. + * Defaults to null, meaning no schema (or the current database). + */ + public $catalogName; + /** + * @var string name of the schema that this table belongs to. + * Defaults to null, meaning no schema (or the current database owner). + */ + public $schemaName; +} diff --git a/web/framework-1.0.8/db/schema/mysql/CMysqlColumnSchema.php b/web/framework-1.0.8/db/schema/mysql/CMysqlColumnSchema.php new file mode 100644 index 0000000..e0c6f71 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mysql/CMysqlColumnSchema.php @@ -0,0 +1,46 @@ +<?php +/** + * CMysqlColumnSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMysqlColumnSchema class describes the column meta data of a MySQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMysqlColumnSchema.php 1196 2009-06-30 15:17:16Z qiang.xue $ + * @package system.db.schema.mysql + * @since 1.0 + */ +class CMysqlColumnSchema extends CDbColumnSchema +{ + /** + * Extracts the PHP type from DB type. + * @param string DB type + */ + protected function extractType($dbType) + { + if(strncmp($dbType,'enum',4)===0) + $this->type='string'; + else if(strpos($dbType,'bigint')!==false || strpos($dbType,'float')!==false || strpos($dbType,'double')!==false) + $this->type='double'; + else if(strpos($dbType,'bool')!==false) + $this->type='boolean'; + else if(strpos($dbType,'int')!==false || strpos($dbType,'bit')!==false) + $this->type='integer'; + else + $this->type='string'; + } + + protected function extractDefault($defaultValue) + { + if($this->dbType==='timestamp' && $defaultValue==='CURRENT_TIMESTAMP') + $this->defaultValue=null; + else + parent::extractDefault($defaultValue); + } +} diff --git a/web/framework-1.0.8/db/schema/mysql/CMysqlSchema.php b/web/framework-1.0.8/db/schema/mysql/CMysqlSchema.php new file mode 100644 index 0000000..4820661 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mysql/CMysqlSchema.php @@ -0,0 +1,201 @@ +<?php +/** + * CMysqlSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMysqlSchema is the class for retrieving metadata information from a MySQL database (version 4.1.x and 5.x). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMysqlSchema.php 631 2009-02-06 12:22:04Z qiang.xue $ + * @package system.db.schema.mysql + * @since 1.0 + */ +class CMysqlSchema extends CDbSchema +{ + private $_tableNames; + private $_schemaNames; + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return '`'.$name.'`'; + } + + /** + * Quotes a column name for use in a query. + * @param string column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return '`'.$name.'`'; + } + + /** + * Compares two table names. + * The table names can be either quoted or unquoted. This method + * will consider both cases. + * @param string table name 1 + * @param string table name 2 + * @return boolean whether the two table names refer to the same table. + */ + public function compareTableNames($name1,$name2) + { + return parent::compareTableNames(strtolower($name1),strtolower($name2)); + } + + /** + * Creates a table instance representing the metadata for the named table. + * @return CMysqlTableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function createTable($name) + { + $table=new CMysqlTableSchema; + $this->resolveTableNames($table,$name); + + if($this->findColumns($table)) + { + $this->findConstraints($table); + return $table; + } + else + return null; + } + + /** + * Generates various kinds of table names. + * @param CMysqlTableSchema the table instance + * @param string the unquoted table name + */ + protected function resolveTableNames($table,$name) + { + $parts=explode('.',str_replace('`','',$name)); + if(isset($parts[1])) + { + $table->schemaName=$parts[0]; + $table->name=$parts[1]; + $table->rawName=$this->quoteTableName($table->schemaName).'.'.$this->quoteTableName($table->name); + } + else + { + $table->name=$parts[0]; + $table->rawName=$this->quoteTableName($table->name); + } + } + + /** + * Collects the table column metadata. + * @param CMysqlTableSchema the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $sql='SHOW COLUMNS FROM '.$table->rawName; + try + { + $columns=$this->getDbConnection()->createCommand($sql)->queryAll(); + } + catch(Exception $e) + { + return false; + } + foreach($columns as $column) + { + $c=$this->createColumn($column); + $table->columns[$c->name]=$c; + if($c->isPrimaryKey) + { + if($table->primaryKey===null) + $table->primaryKey=$c->name; + else if(is_string($table->primaryKey)) + $table->primaryKey=array($table->primaryKey,$c->name); + else + $table->primaryKey[]=$c->name; + if(strpos(strtolower($column['Extra']),'auto_increment')!==false) + $table->sequenceName=''; + } + } + return true; + } + + /** + * Creates a table column. + * @param array column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c=new CMysqlColumnSchema; + $c->name=$column['Field']; + $c->rawName=$this->quoteColumnName($c->name); + $c->allowNull=$column['Null']==='YES'; + $c->isPrimaryKey=strpos($column['Key'],'PRI')!==false; + $c->isForeignKey=false; + $c->init($column['Type'],$column['Default']); + return $c; + } + + /** + * @return float server version. + */ + protected function getServerVersion() + { + $version=$this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION); + $digits=array(); + preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits); + return floatval($digits[1].'.'.$digits[2].$digits[3]); + } + + /** + * Collects the foreign key column details for the given table. + * @param CMysqlTableSchema the table metadata + */ + protected function findConstraints($table) + { + $row=$this->getDbConnection()->createCommand('SHOW CREATE TABLE '.$table->rawName)->queryRow(); + $matches=array(); + $regexp='/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; + foreach($row as $sql) + { + if(preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER)) + break; + } + $foreign = array(); + foreach($matches as $match) + { + $keys=array_map('trim',explode(',',str_replace('`','',$match[1]))); + $fks=array_map('trim',explode(',',str_replace('`','',$match[3]))); + foreach($keys as $k=>$name) + { + $table->foreignKeys[$name]=array(str_replace('`','',$match[2]),$fks[$k]); + if(isset($table->columns[$name])) + $table->columns[$name]->isForeignKey=true; + } + } + } + + /** + * Returns all table names in the database. + * @return array all table names in the database. + * @since 1.0.2 + */ + protected function findTableNames($schema='') + { + if($schema==='') + return $this->getDbConnection()->createCommand('SHOW TABLES')->queryColumn(); + $names=$this->getDbConnection()->createCommand('SHOW TABLES FROM '.$this->quoteTableName($schema))->queryColumn(); + foreach($names as &$name) + $name=$schema.'.'.$name; + return $names; + } +} diff --git a/web/framework-1.0.8/db/schema/mysql/CMysqlTableSchema.php b/web/framework-1.0.8/db/schema/mysql/CMysqlTableSchema.php new file mode 100644 index 0000000..6423280 --- /dev/null +++ b/web/framework-1.0.8/db/schema/mysql/CMysqlTableSchema.php @@ -0,0 +1,26 @@ +<?php +/** + * CMysqlTableSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMysqlTableSchema represents the metadata for a MySQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMysqlTableSchema.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db.schema.mysql + * @since 1.0 + */ +class CMysqlTableSchema extends CDbTableSchema +{ + /** + * @var string name of the schema (database) that this table belongs to. + * Defaults to null, meaning no schema (or the current database). + */ + public $schemaName; +} diff --git a/web/framework-1.0.8/db/schema/oci/COciColumnSchema.php b/web/framework-1.0.8/db/schema/oci/COciColumnSchema.php new file mode 100644 index 0000000..ab4acf0 --- /dev/null +++ b/web/framework-1.0.8/db/schema/oci/COciColumnSchema.php @@ -0,0 +1,54 @@ +<?php +/** + * COciColumnSchema class file. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COciColumnSchema class describes the column meta data of a Oracle table. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @version $Id: COciColumnSchema.php + * @package system.db.schema.oci + * @since 1.0.5 + */ +class COciColumnSchema extends CDbColumnSchema +{ + /** + * Extracts the PHP type from DB type. + * @param string DB type + */ + protected function extractOraType($dbType){ + if(strpos($dbType,'FLOAT')!==false) return 'double'; + + if ((strpos($dbType,'NUMBER')!==false) or + (strpos($dbType,'INTEGER')!==false)) + { + if(strpos($dbType,'(') && preg_match('/\((.*)\)/',$dbType,$matches)) + { + $values=explode(',',$matches[1]); + if(isset($values[1]) and (((int)$values[1]) > 0)) + return 'double'; + else return 'integer'; + } + }else{ + return 'string'; + } + } + protected function extractType($dbType) + { + $this->type=$this->extractOraType($dbType); + } + + protected function extractDefault($defaultValue) + { + if(stripos($defaultValue,'timestamp')!==false) + $this->defaultValue=null; + else + parent::extractDefault($defaultValue); + } +} diff --git a/web/framework-1.0.8/db/schema/oci/COciCommandBuilder.php b/web/framework-1.0.8/db/schema/oci/COciCommandBuilder.php new file mode 100644 index 0000000..d75c260 --- /dev/null +++ b/web/framework-1.0.8/db/schema/oci/COciCommandBuilder.php @@ -0,0 +1,122 @@ +<?php +/** + * COciCommandBuilder class file. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COciCommandBuilder provides basic methods to create query commands for tables. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @version $Id: COciCommandBuilder.php 1281 2009-07-31 21:40:12Z qiang.xue $ + * @package system.db.schema.oci + * @since 1.0.5 + */ +class COciCommandBuilder extends CDbCommandBuilder +{ + /** + * @var integer the last insertion ID + */ + public $returnID; + + /** + * Returns the last insertion ID for the specified table. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @return mixed last insertion id. Null is returned if no sequence name. + */ + public function getLastInsertID($table) + { + return $this->returnID; + } + + /** + * Alters the SQL to apply LIMIT and OFFSET. + * Default implementation is applicable for PostgreSQL, MySQL and SQLite. + * @param string SQL query string without LIMIT and OFFSET. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with LIMIT and OFFSET + */ + public function applyLimit($sql,$limit,$offset) + { + if (($limit < 0) and ($offset < 0)) return $sql; + + $filters = array(); + if($offset>0){ + $filters[] = 'rowNumId > '.(int)$offset; + } + + if($limit>=0){ + $filters[]= 'rownum <= '.(int)$limit; + } + + if (count($filters) > 0){ + $filter = implode(' and ', $filters); + $filter= " WHERE ".$filter; + }else{ + $filter = ''; + } + + + $sql = <<<EOD + WITH USER_SQL AS ({$sql}), + PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL) + SELECT * + FROM PAGINATION + {$filter} +EOD; + + return $sql; + } + + /** + * Creates an INSERT command. + * @param mixed the table schema ({@link CDbTableSchema}) or the table name (string). + * @param array data to be inserted (column name=>column value). If a key is not a valid column name, the corresponding value will be ignored. + * @return CDbCommand insert command + */ + public function createInsertCommand($table,$data) + { + $this->ensureTable($table); + $fields=array(); + $values=array(); + $placeholders=array(); + $i=0; + foreach($data as $name=>$value) + { + if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull)) + { + $fields[]=$column->rawName; + if($value instanceof CDbExpression) + $placeholders[]=$value->expression; + else + { + $placeholders[]=self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; + } + } + } + + $sql="INSERT INTO {$table->rawName} (".implode(', ',$fields).') VALUES ('.implode(', ',$placeholders).')'; + + if(is_string($table->primaryKey)) + { + $sql.=" RETURNING ".$table->primaryKey." INTO :RETURN_ID"; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindParam(':RETURN_ID', $this->returnID, PDO::PARAM_INT, 12); + $table->sequenceName='RETURN_ID'; + } + else + $command=$this->getDbConnection()->createCommand($sql); + + foreach($values as $name=>$value) + $command->bindValue($name,$value); + + return $command; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/db/schema/oci/COciSchema.php b/web/framework-1.0.8/db/schema/oci/COciSchema.php new file mode 100644 index 0000000..c5eea52 --- /dev/null +++ b/web/framework-1.0.8/db/schema/oci/COciSchema.php @@ -0,0 +1,276 @@ +<?php +/** + * COciSchema class file. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COciSchema is the class for retrieving metadata information from a PostgreSQL database. + * + * @author Ricardo Grana <qiang.xue@gmail.com> + * @version $Id: COciSchema.php 1108 2009-06-09 01:48:40Z qiang.xue $ + * @package system.db.schema.oci + * @since 1.0.5 + */ +class COciSchema extends CDbSchema +{ + private $_defaultSchema = ''; + private $_sequences=array(); + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return $name; + } + + /** + * Quotes a column name for use in a query. + * @param string column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return $name; + } + + /** + * Creates a command builder for the database. + * This method may be overridden by child classes to create a DBMS-specific command builder. + * @return CDbCommandBuilder command builder instance + */ + protected function createCommandBuilder() + { + return new COciCommandBuilder($this); + } + + /** + * @param string default schema. + */ + public function setDefaultSchema($schema) + { + $this->_defaultSchema=$schema; + } + + /** + * @return string default schema. + */ + public function getDefaultSchema() + { + if (!strlen($this->_defaultSchema)) + { + $this->setDefaultSchema(strtoupper($this->getDbConnection()->username)); + } + + return $this->_defaultSchema; + } + + /** + * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. + * @return array tuple as ($schemaName,$tableName) + */ + protected function getSchemaTableName($table) + { + $table = strtoupper($table); + if(count($parts= explode('.', str_replace('"','',$table))) > 1) + return array($parts[0], $parts[1]); + else + return array($this->getDefaultSchema(),$parts[0]); + } + + /** + * Creates a table instance representing the metadata for the named table. + * @return CDbTableSchema driver dependent table metadata. + */ + protected function createTable($name) + { + $table=new COciTableSchema; + $this->resolveTableNames($table,$name); + + if(!$this->findColumns($table)) + return null; + $this->findConstraints($table); + + return $table; + } + + /** + * Generates various kinds of table names. + * @param COciTableSchema the table instance + * @param string the unquoted table name + */ + protected function resolveTableNames($table,$name) + { + $parts=explode('.',str_replace('"','',$name)); + if(isset($parts[1])) + { + $schemaName=$parts[0]; + $tableName=$parts[1]; + } + else + { + $schemaName=$this->getDefaultSchema(); + $tableName=$parts[0]; + } + + $table->name=$tableName; + $table->schemaName=$schemaName; + if($schemaName===$this->getDefaultSchema()) + $table->rawName=$this->quoteTableName($tableName); + else + $table->rawName=$this->quoteTableName($schemaName).'.'.$this->quoteTableName($tableName); + } + + /** + * Collects the table column metadata. + * @param COciTableSchema the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table->name); + + $sql=<<<EOD +SELECT a.column_name, a.data_type || + case + when data_precision is not null + then '(' || a.data_precision || + case when a.data_scale > 0 then ',' || a.data_scale else '' end + || ')' + when data_type = 'DATE' then '' + else '(' || to_char(a.data_length) || ')' + end as data_type, + a.nullable, a.data_default, + ( SELECT D.constraint_type + FROM ALL_CONS_COLUMNS C + inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name + WHERE C.OWNER = B.OWNER + and C.table_name = B.object_name + and C.column_name = A.column_name + and D.constraint_type = 'P') as Key +FROM ALL_TAB_COLUMNS A +inner join ALL_OBJECTS B ON b.owner = a.owner and ltrim(B.OBJECT_NAME) = ltrim(A.TABLE_NAME) +WHERE + a.owner = '{$schemaName}' + and b.object_type = 'TABLE' + and b.object_name = '{$tableName}' +ORDER by a.column_id +EOD; + + $command=$this->getDbConnection()->createCommand($sql); + + if(($columns=$command->queryAll())===array()){ + return false; + } + + foreach($columns as $column) + { + $c=$this->createColumn($column); + + $table->columns[$c->name]=$c; + if($c->isPrimaryKey) + { + if($table->primaryKey===null) + $table->primaryKey=$c->name; + else if(is_string($table->primaryKey)) + $table->primaryKey=array($table->primaryKey,$c->name); + else + $table->primaryKey[]=$c->name; + } + } + return true; + } + + /** + * Creates a table column. + * @param array column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c=new COciColumnSchema; + $c->name=$column['COLUMN_NAME']; + $c->rawName=$this->quoteColumnName($c->name); + $c->allowNull=$column['NULLABLE']==='Y'; + $c->isPrimaryKey=strpos($column['KEY'],'P')!==false; + $c->isForeignKey=false; + $c->init($column['DATA_TYPE'],$column['DATA_DEFAULT']); + + return $c; + } + + /** + * Collects the primary and foreign key column details for the given table. + * @param COciTableSchema the table metadata + */ + protected function findConstraints($table) + { + $sql=<<<EOD + SELECT D.constraint_type as CONSTRAINT_TYPE, C.COLUMN_NAME, C.position, D.r_constraint_name, + E.table_name as table_ref, f.column_name as column_ref + FROM ALL_CONS_COLUMNS C + inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name + left join ALL_constraints E on E.OWNER = D.r_OWNER and E.constraint_name = D.r_constraint_name + left join ALL_cons_columns F on F.OWNER = E.OWNER and F.constraint_name = E.constraint_name and F.position = c.position + WHERE C.OWNER = '{$table->schemaName}' + and C.table_name = '{$table->name}' + and D.constraint_type <> 'P' + order by d.constraint_name, c.position +EOD; + $command=$this->getDbConnection()->createCommand($sql); + foreach($command->queryAll() as $row) + { + if($row['CONSTRAINT_TYPE']==='R') // foreign key + { + $name = $row["COLUMN_NAME"]; + $table->foreignKeys[$name]=array($row["TABLE_REF"], array($row["COLUMN_REF"])); + if(isset($table->columns[$name])) + $table->columns[$name]->isForeignKey=true; + } + + } + } + + + /** + * Returns all table names in the database. + * @return array all table names in the database. + */ + protected function findTableNames($schema='') + { + if($schema==='') + { + $sql=<<<EOD +SELECT table_name, '{$schema}' as table_schema FROM user_tables +EOD; + $command=$this->getDbConnection()->createCommand($sql); + } + else + { + $sql=<<<EOD +SELECT object_name as table_name, owner as table_schema FROM all_objects +WHERE object_type = 'TABLE' AND owner=:schema +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindParam(':schema',$schema); + } + + $rows=$command->queryAll(); + $names=array(); + foreach($rows as $row) + { + if($schema===$this->getDefaultSchema()) + $names[]=$row['table_name']; + else + $names[]=$row['schema_name'].'.'.$row['table_name']; + } + return $names; + } +} diff --git a/web/framework-1.0.8/db/schema/oci/COciTableSchema.php b/web/framework-1.0.8/db/schema/oci/COciTableSchema.php new file mode 100644 index 0000000..f92dc20 --- /dev/null +++ b/web/framework-1.0.8/db/schema/oci/COciTableSchema.php @@ -0,0 +1,26 @@ +<?php +/** + * COciTableSchema class file. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COciTableSchema represents the metadata for a Ora table. + * + * @author Ricardo Grana <rickgrana@yahoo.com.br> + * @version $Id: COciTableSchema.php 939 2009-04-15 19:37:34Z qiang.xue@gmail.com $ + * @package system.db.schema.oci + * @since 1.0.5 + */ +class COciTableSchema extends CDbTableSchema +{ + /** + * @var string name of the schema (database) that this table belongs to. + * Defaults to null, meaning no schema (or the current database). + */ + public $schemaName; +} diff --git a/web/framework-1.0.8/db/schema/pgsql/CPgsqlColumnSchema.php b/web/framework-1.0.8/db/schema/pgsql/CPgsqlColumnSchema.php new file mode 100644 index 0000000..a98a392 --- /dev/null +++ b/web/framework-1.0.8/db/schema/pgsql/CPgsqlColumnSchema.php @@ -0,0 +1,56 @@ +<?php +/** + * CPgsqlColumnSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPgsqlColumnSchema class describes the column meta data of a PostgreSQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CPgsqlColumnSchema.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db.schema.pgsql + * @since 1.0 + */ +class CPgsqlColumnSchema extends CDbColumnSchema +{ + /** + * Extracts the PHP type from DB type. + * @param string DB type + */ + protected function extractType($dbType) + { + if(strpos($dbType,'integer')!==false || strpos($dbType,'oid')===0) + $this->type='integer'; + else if(strpos($dbType,'bool')!==false) + $this->type='boolean'; + else if(preg_match('/(real|float|double)/',$dbType)) + $this->type='double'; + else + $this->type='string'; + } + + /** + * Extracts the default value for the column. + * The value is typecasted to correct PHP type. + * @param mixed the default value obtained from metadata + */ + protected function extractDefault($defaultValue) + { + if($defaultValue==='true') + $this->defaultValue=true; + else if($defaultValue==='false') + $this->defaultValue=false; + else if(strpos($defaultValue,'nextval')===0) + $this->defaultValue=null; + else if(preg_match('/\'(.*)\'::/',$defaultValue,$matches)) + $this->defaultValue=$this->typecast(str_replace("''","'",$matches[1])); + else if(preg_match('/^-?\d+(\.\d*)?$/',$defaultValue,$matches)) + $this->defaultValue=$this->typecast($defaultValue); + // else is null + } +} diff --git a/web/framework-1.0.8/db/schema/pgsql/CPgsqlSchema.php b/web/framework-1.0.8/db/schema/pgsql/CPgsqlSchema.php new file mode 100644 index 0000000..161c879 --- /dev/null +++ b/web/framework-1.0.8/db/schema/pgsql/CPgsqlSchema.php @@ -0,0 +1,282 @@ +<?php +/** + * CPgsqlSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPgsqlSchema is the class for retrieving metadata information from a PostgreSQL database. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CPgsqlSchema.php 1111 2009-06-10 19:53:42Z qiang.xue $ + * @package system.db.schema.pgsql + * @since 1.0 + */ +class CPgsqlSchema extends CDbSchema +{ + const DEFAULT_SCHEMA='public'; + private $_sequences=array(); + + /** + * Quotes a table name for use in a query. + * @param string table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return '"'.$name.'"'; + } + + /** + * Creates a table instance representing the metadata for the named table. + * @return CDbTableSchema driver dependent table metadata. + */ + protected function createTable($name) + { + $table=new CPgsqlTableSchema; + $this->resolveTableNames($table,$name); + if(!$this->findColumns($table)) + return null; + $this->findConstraints($table); + + if(is_string($table->primaryKey) && isset($this->_sequences[$table->primaryKey])) + $table->sequenceName=$this->_sequences[$table->primaryKey]; + + return $table; + } + + /** + * Generates various kinds of table names. + * @param CPgsqlTableSchema the table instance + * @param string the unquoted table name + */ + protected function resolveTableNames($table,$name) + { + $parts=explode('.',str_replace('"','',$name)); + if(isset($parts[1])) + { + $schemaName=$parts[0]; + $tableName=$parts[1]; + } + else + { + $schemaName=self::DEFAULT_SCHEMA; + $tableName=$parts[0]; + } + + $table->name=$tableName; + $table->schemaName=$schemaName; + if($schemaName===self::DEFAULT_SCHEMA) + $table->rawName=$this->quoteTableName($tableName); + else + $table->rawName=$this->quoteTableName($schemaName).'.'.$this->quoteTableName($tableName); + } + + /** + * Collects the table column metadata. + * @param CPgsqlTableSchema the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $sql=<<<EOD +SELECT a.attname, LOWER(format_type(a.atttypid, a.atttypmod)) AS type, d.adsrc, a.attnotnull, a.atthasdef +FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum +WHERE a.attnum > 0 AND NOT a.attisdropped + AND a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table + AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = :schema)) +ORDER BY a.attnum +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':table',$table->name); + $command->bindValue(':schema',$table->schemaName); + + if(($columns=$command->queryAll())===array()) + return false; + + foreach($columns as $column) + { + $c=$this->createColumn($column); + $table->columns[$c->name]=$c; + + if(stripos($column['adsrc'],'nextval')===0 && preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$column['adsrc'],$matches)) + { + if(strpos($matches[1],'.')!==false || $table->schemaName===self::DEFAULT_SCHEMA) + $this->_sequences[$c->name]=$matches[1]; + else + $this->_sequences[$c->name]=$table->schemaName.'.'.$matches[1]; + } + } + return true; + } + + /** + * Creates a table column. + * @param array column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c=new CPgsqlColumnSchema; + $c->name=$column['attname']; + $c->rawName=$this->quoteColumnName($c->name); + $c->allowNull=!$column['attnotnull']; + $c->isPrimaryKey=false; + $c->isForeignKey=false; + + $c->init($column['type'],$column['atthasdef'] ? $column['adsrc'] : null); + + return $c; + } + + /** + * Collects the primary and foreign key column details for the given table. + * @param CPgsqlTableSchema the table metadata + */ + protected function findConstraints($table) + { + $sql=<<<EOD +SELECT conname, consrc, contype, indkey FROM ( + SELECT + conname, + CASE WHEN contype='f' THEN + pg_catalog.pg_get_constraintdef(oid) + ELSE + 'CHECK (' || consrc || ')' + END AS consrc, + contype, + conrelid AS relid, + NULL AS indkey + FROM + pg_catalog.pg_constraint + WHERE + contype IN ('f', 'c') + UNION ALL + SELECT + pc.relname, + NULL, + CASE WHEN indisprimary THEN + 'p' + ELSE + 'u' + END, + pi.indrelid, + indkey + FROM + pg_catalog.pg_class pc, + pg_catalog.pg_index pi + WHERE + pc.oid=pi.indexrelid + AND EXISTS ( + SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c + ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) + WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p') + ) +) AS sub +WHERE relid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table + AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace + WHERE nspname=:schema)) +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':table',$table->name); + $command->bindValue(':schema',$table->schemaName); + foreach($command->queryAll() as $row) + { + if($row['contype']==='p') // primary key + $this->findPrimaryKey($table,$row['indkey']); + else if($row['contype']==='f') // foreign key + $this->findForeignKey($table,$row['consrc']); + } + } + + /** + * Collects primary key information. + * @param CPgsqlTableSchema the table metadata + * @param string pgsql primary key index list + */ + protected function findPrimaryKey($table,$indices) + { + $indices=implode(', ',preg_split('/\s+/',$indices)); + $sql=<<<EOD +SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE + attrelid=( + SELECT oid FROM pg_catalog.pg_class WHERE relname=:table AND relnamespace=( + SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=:schema + ) + ) + AND attnum IN ({$indices}) +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':table',$table->name); + $command->bindValue(':schema',$table->schemaName); + foreach($command->queryAll() as $row) + { + $name=$row['attname']; + if(isset($table->columns[$name])) + { + $table->columns[$name]->isPrimaryKey=true; + if($table->primaryKey===null) + $table->primaryKey=$name; + else if(is_string($table->primaryKey)) + $table->primaryKey=array($table->primaryKey,$name); + else + $table->primaryKey[]=$name; + } + } + } + + /** + * Collects foreign key information. + * @param CPgsqlTableSchema the table metadata + * @param string pgsql foreign key definition + */ + protected function findForeignKey($table,$src) + { + $matches=array(); + $brackets='\(([^\)]+)\)'; + $pattern="/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i"; + if(preg_match($pattern,str_replace('"','',$src),$matches)) + { + $keys=preg_split('/,\s+/', $matches[1]); + $tableName=$matches[2]; + $fkeys=preg_split('/,\s+/', $matches[3]); + foreach($keys as $i=>$key) + { + $table->foreignKeys[$key]=array($tableName,$fkeys[$i]); + if(isset($table->columns[$key])) + $table->columns[$key]->isForeignKey=true; + } + } + } + + /** + * Returns all table names in the database. + * @return array all table names in the database. + * @since 1.0.2 + */ + protected function findTableNames($schema='') + { + if($schema==='') + $schema=self::DEFAULT_SCHEMA; + $sql=<<<EOD +SELECT table_name, table_schema FROM information_schema.tables +WHERE table_schema=:schema +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindParam(':schema',$schema); + $rows=$command->queryAll(); + $names=array(); + foreach($rows as $row) + { + if($schema===self::DEFAULT_SCHEMA) + $names[]=$row['table_name']; + else + $names[]=$row['table_schema'].'.'.$row['table_name']; + } + return $names; + } +} diff --git a/web/framework-1.0.8/db/schema/pgsql/CPgsqlTableSchema.php b/web/framework-1.0.8/db/schema/pgsql/CPgsqlTableSchema.php new file mode 100644 index 0000000..2fbca2f --- /dev/null +++ b/web/framework-1.0.8/db/schema/pgsql/CPgsqlTableSchema.php @@ -0,0 +1,25 @@ +<?php +/** + * CPgsqlTable class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPgsqlTable represents the metadata for a PostgreSQL table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CPgsqlTableSchema.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db.schema.pgsql + * @since 1.0 + */ +class CPgsqlTableSchema extends CDbTableSchema +{ + /** + * @var string name of the schema that this table belongs to. + */ + public $schemaName; +} diff --git a/web/framework-1.0.8/db/schema/sqlite/CSqliteColumnSchema.php b/web/framework-1.0.8/db/schema/sqlite/CSqliteColumnSchema.php new file mode 100644 index 0000000..5db5559 --- /dev/null +++ b/web/framework-1.0.8/db/schema/sqlite/CSqliteColumnSchema.php @@ -0,0 +1,33 @@ +<?php +/** + * CSqliteColumnSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CSqliteColumnSchema class describes the column meta data of a SQLite table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CSqliteColumnSchema.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.db.schema.sqlite + * @since 1.0 + */ +class CSqliteColumnSchema extends CDbColumnSchema +{ + /** + * Extracts the default value for the column. + * The value is typecasted to correct PHP type. + * @param mixed the default value obtained from metadata + */ + protected function extractDefault($defaultValue) + { + if($this->type==='string') // PHP 5.2.6 adds single quotes while 5.2.0 doesn't + $this->defaultValue=trim($defaultValue,"'\""); + else + $this->defaultValue=$this->typecast($defaultValue); + } +} diff --git a/web/framework-1.0.8/db/schema/sqlite/CSqliteCommandBuilder.php b/web/framework-1.0.8/db/schema/sqlite/CSqliteCommandBuilder.php new file mode 100644 index 0000000..0bbeb52 --- /dev/null +++ b/web/framework-1.0.8/db/schema/sqlite/CSqliteCommandBuilder.php @@ -0,0 +1,41 @@ +<?php +/** + * CSqliteCommandBuilder class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CSqliteCommandBuilder provides basic methods to create query commands for SQLite tables. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CSqliteCommandBuilder.php 840 2009-03-15 22:36:06Z qiang.xue $ + * @package system.db.schema.sqlite + * @since 1.0 + */ +class CSqliteCommandBuilder extends CDbCommandBuilder +{ + /** + * Generates the expression for selecting rows with specified composite key values. + * This method is overridden because SQLite does not support the default + * IN expression with composite columns. + * @param CDbTableSchema the table schema + * @param array list of primary key values to be selected within + * @param string column prefix (ended with dot) + * @return string the expression for selection + * @since 1.0.4 + */ + protected function createCompositeInCondition($table,$values,$prefix) + { + $keyNames=array(); + foreach(array_keys($values[0]) as $name) + $keyNames[]=$prefix.$table->columns[$name]->rawName; + $vs=array(); + foreach($values as $value) + $vs[]=implode("||','||",$value); + return implode("||','||",$keyNames).' IN ('.implode(', ',$vs).')'; + } +} diff --git a/web/framework-1.0.8/db/schema/sqlite/CSqliteSchema.php b/web/framework-1.0.8/db/schema/sqlite/CSqliteSchema.php new file mode 100644 index 0000000..e0a5a0d --- /dev/null +++ b/web/framework-1.0.8/db/schema/sqlite/CSqliteSchema.php @@ -0,0 +1,129 @@ +<?php +/** + * CSqliteSchema class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CSqliteSchema is the class for retrieving metadata information from a SQLite (2/3) database. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CSqliteSchema.php 487 2009-01-08 03:44:52Z qiang.xue $ + * @package system.db.schema.sqlite + * @since 1.0 + */ +class CSqliteSchema extends CDbSchema +{ + /** + * Returns all table names in the database. + * @param string the schema of the tables. This is not used for sqlite database. + * @return array all table names in the database. + * @since 1.0.2 + */ + protected function findTableNames($schema='') + { + $sql="SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'"; + return $this->getDbConnection()->createCommand($sql)->queryColumn(); + } + + /** + * Creates a command builder for the database. + * @return CSqliteCommandBuilder command builder instance + */ + protected function createCommandBuilder() + { + return new CSqliteCommandBuilder($this); + } + + /** + * Creates a table instance representing the metadata for the named table. + * @return CDbTableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function createTable($name) + { + $db=$this->getDbConnection(); + + $table=new CDbTableSchema; + $table->name=$name; + $table->rawName=$this->quoteTableName($name); + + if($this->findColumns($table)) + { + $this->findConstraints($table); + return $table; + } + else + return null; + } + + /** + * Collects the table column metadata. + * @param CDbTableSchema the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $sql="PRAGMA table_info({$table->rawName})"; + $columns=$this->getDbConnection()->createCommand($sql)->queryAll(); + if(empty($columns)) + return false; + + foreach($columns as $column) + { + $c=$this->createColumn($column); + $table->columns[$c->name]=$c; + if($c->isPrimaryKey) + { + if($table->primaryKey===null) + $table->primaryKey=$c->name; + else if(is_string($table->primaryKey)) + $table->primaryKey=array($table->primaryKey,$c->name); + else + $table->primaryKey[]=$c->name; + } + } + if(is_string($table->primaryKey) && !strncasecmp($table->columns[$table->primaryKey]->dbType,'int',3)) + $table->sequenceName=''; + + return true; + } + + /** + * Collects the foreign key column details for the given table. + * @param CDbTableSchema the table metadata + */ + protected function findConstraints($table) + { + $foreignKeys=array(); + $sql="PRAGMA foreign_key_list({$table->rawName})"; + $keys=$this->getDbConnection()->createCommand($sql)->queryAll(); + foreach($keys as $key) + { + $column=$table->columns[$key['from']]; + $column->isForeignKey=true; + $foreignKeys[$key['from']]=array($key['table'],$key['to']); + } + $table->foreignKeys=$foreignKeys; + } + + /** + * Creates a table column. + * @param array column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c=new CSqliteColumnSchema; + $c->name=$column['name']; + $c->rawName=$this->quoteColumnName($c->name); + $c->allowNull=!$column['notnull']; + $c->isPrimaryKey=$column['pk']!=0; + $c->isForeignKey=false; + $c->init(strtolower($column['type']),$column['dflt_value']); + return $c; + } +} diff --git a/web/framework-1.0.8/i18n/CChoiceFormat.php b/web/framework-1.0.8/i18n/CChoiceFormat.php new file mode 100644 index 0000000..cf6fbf0 --- /dev/null +++ b/web/framework-1.0.8/i18n/CChoiceFormat.php @@ -0,0 +1,77 @@ +<?php +/** + * YiiBase class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CChoiceFormat is a helper that chooses an appropriate message based on the specified number value. + * The candidate messages are given as a string in the following format: + * <pre> + * 'expr1#message1|expr2#message2|expr3#message3' + * </pre> + * where each expression should be a valid PHP expression with 'n' as the only variable. + * For example, 'n==1' and 'n%10==2 && n>10' are both valid expressions. + * The variable 'n' will take the given number value, and if an expression evaluates true, + * the corresponding message will be returned. + * + * For example, given the candidate messages 'n==1#one|n==2#two|n>2#others' and + * the number value 2, the resulting message will be 'two'. + * + * For expressions like 'n==1', we can also use a shortcut '1'. So the above example + * candidate messages can be simplified as '1#one|2#two|n>2#others'. + * + * In case the given number doesn't select any message, the last candidate message + * will be returned. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CChoiceFormat.php 570 2009-01-24 19:02:46Z qiang.xue $ + * @package system.utils + * @since 1.0.2 + */ +class CChoiceFormat +{ + /** + * Formats a message according to the specified number value. + * @param string the candidate messages in the format of 'expr1#message1|expr2#message2|expr3#message3'. + * See {@link CChoiceFormat} for more details. + * @param mixed the number value + * @return string the selected message + */ + public static function format($messages, $number) + { + $n=preg_match_all('/\s*([^#]*)\s*#([^\|]*)\|/',$messages.'|',$matches); + if($n===0) + return $messages; + for($i=0;$i<$n;++$i) + { + $expression=$matches[1][$i]; + $message=$matches[2][$i]; + $intval=(int)$expression; + if($expression==="$intval") + { + if($intval==$number) + return $message; + } + else if(self::evaluate(str_replace('n','$n',$expression),$number)) + return $message; + } + return $message; // return the last choice + } + + /** + * Evaluates a PHP expression with the given number value. + * @param string the PHP expression + * @param mixed the number value + * @return boolean the expression result + */ + protected static function evaluate($expression,$n) + { + return @eval("return $expression;"); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/i18n/CDateFormatter.php b/web/framework-1.0.8/i18n/CDateFormatter.php new file mode 100644 index 0000000..a7e50dd --- /dev/null +++ b/web/framework-1.0.8/i18n/CDateFormatter.php @@ -0,0 +1,507 @@ +<?php +/** + * CDateFormatter class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDateFormatter provides date/time localization functionalities. + * + * CDateFormatter allows you to format dates and times in a locale-sensitive manner. + * Patterns are interpretted in the locale that the CDateFormatter instance + * is associated with. For example, month names and weekday names may vary + * under different locales, which yields different formatting results. + * The patterns that CDateFormatter recognizes are as defined in + * {@link http://www.unicode.org/reports/tr35/#Date_Format_Patterns CLDR}. + * + * CDateFormatter supports predefined patterns as well as customized ones: + * <ul> + * <li>The method {@link formatDateTime()} formats date or time or both using + * predefined patterns which include 'full', 'long', 'medium' (default) and 'short'.</li> + * <li>The method {@link format()} formats datetime using the specified pattern. + * See {@link http://www.unicode.org/reports/tr35/#Date_Format_Patterns} for + * details about the recognized pattern characters.</li> + * </ul> + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDateFormatter.php 926 2009-04-06 18:12:09Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CDateFormatter extends CComponent +{ + /** + * @var array pattern characters mapping to the corresponding translator methods + */ + private static $_formatters=array( + 'G'=>'formatEra', + 'y'=>'formatYear', + 'M'=>'formatMonth', + 'd'=>'formatDay', + 'h'=>'formatHour12', + 'H'=>'formatHour24', + 'm'=>'formatMinutes', + 's'=>'formatSeconds', + 'E'=>'formatDayInWeek', + 'D'=>'formatDayInYear', + 'F'=>'formatDayInMonth', + 'w'=>'formatWeekInYear', + 'W'=>'formatWeekInMonth', + 'a'=>'formatPeriod', + 'k'=>'formatHourInDay', + 'K'=>'formatHourInPeriod', + 'z'=>'formatTimeZone', + 'v'=>'formatTimeZone', + ); + + private $_locale; + + /** + * Constructor. + * @param mixed locale ID (string) or CLocale instance + */ + public function __construct($locale) + { + if(is_string($locale)) + $this->_locale=CLocale::getInstance($locale); + else + $this->_locale=$locale; + } + + /** + * Formats a date according to a customized pattern. + * @param string the pattern (See {@link http://www.unicode.org/reports/tr35/#Date_Format_Patterns}) + * @param mixed UNIX timestamp or a string in strtotime format + * @return string formatted date time. + */ + public function format($pattern,$time) + { + if(is_string($time)) + { + if(ctype_digit($time)) + $time=(int)$time; + else + $time=strtotime($time); + } + $date=CTimestamp::getDate($time,false,false); + $tokens=$this->parseFormat($pattern); + foreach($tokens as &$token) + { + if(is_array($token)) // a callback: method name, sub-pattern + $token=$this->{$token[0]}($token[1],$date); + } + return implode('',$tokens); + } + + /** + * Formats a date according to a predefined pattern. + * The predefined pattern is determined based on the date pattern width and time pattern width. + * @param mixed UNIX timestamp or a string in strtotime format + * @param string width of the date pattern. It can be 'full', 'long', 'medium' and 'short'. + * If null, it means the date portion will NOT appear in the formatting result + * @param string width of the time pattern. It can be 'full', 'long', 'medium' and 'short'. + * If null, it means the time portion will NOT appear in the formatting result + * @return string formatted date time. + */ + public function formatDateTime($timestamp,$dateWidth='medium',$timeWidth='medium') + { + if(!empty($dateWidth)) + $date=$this->format($this->_locale->getDateFormat($dateWidth),$timestamp); + + if(!empty($timeWidth)) + $time=$this->format($this->_locale->getTimeFormat($timeWidth),$timestamp); + + if(isset($date) && isset($time)) + { + $dateTimePattern=$this->_locale->getDateTimeFormat(); + return strtr($dateTimePattern,array('{0}'=>$time,'{1}'=>$date)); + } + else if(isset($date)) + return $date; + else if(isset($time)) + return $time; + } + + /** + * Parses the datetime format pattern. + * @param string the pattern to be parsed + * @return array tokenized parsing result + */ + protected function parseFormat($pattern) + { + static $formats=array(); // cache + if(isset($formats[$pattern])) + return $formats[$pattern]; + $tokens=array(); + $n=strlen($pattern); + $isLiteral=false; + $literal=''; + for($i=0;$i<$n;++$i) + { + $c=$pattern[$i]; + if($c==="'") + { + if($i<$n-1 && $pattern[$i+1]==="'") + { + $tokens[]="'"; + $i++; + } + else if($isLiteral) + { + $tokens[]=$literal; + $literal=''; + $isLiteral=false; + } + else + { + $isLiteral=true; + $literal=''; + } + } + else if($isLiteral) + $literal.=$c; + else + { + for($j=$i+1;$j<$n;++$j) + { + if($pattern[$j]!==$c) + break; + } + $p=str_repeat($c,$j-$i); + if(isset(self::$_formatters[$c])) + $tokens[]=array(self::$_formatters[$c],$p); + else + $tokens[]=$p; + $i=$j-1; + } + } + if($literal!=='') + $tokens[]=$literal; + + return $formats[$pattern]=$tokens; + } + + /** + * Get the year. + * "yy" will return the last two digits of year. + * "y...y" will pad the year with 0 in the front, e.g. "yyyyy" will generate "02008" for year 2008. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string formatted year + */ + protected function formatYear($pattern,$date) + { + $year=$date['year']; + if($pattern==='yy') + return str_pad($year%100,2,'0',STR_PAD_LEFT); + else + return str_pad($year,strlen($pattern),'0',STR_PAD_LEFT); + } + + /** + * Get the month. + * "M" will return integer 1 through 12; + * "MM" will return two digits month number with necessary zero padding, e.g. 05; + * "MMM" will return the abrreviated month name, e.g. "Jan"; + * "MMMM" will return the full month name, e.g. "January"; + * "MMMMM" will return the narrow month name, e.g. "J"; + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string month name + */ + protected function formatMonth($pattern,$date) + { + $month=$date['mon']; + switch($pattern) + { + case 'M': + return $month; + case 'MM': + return str_pad($month,2,'0',STR_PAD_LEFT); + case 'MMM': + return $this->_locale->getMonthName($month,'abbreviated'); + case 'MMMM': + return $this->_locale->getMonthName($month,'wide'); + case 'MMMMM': + return $this->_locale->getMonthName($month,'narrow'); + default: + throw new CException(Yii::t('yii','The pattern for month must be "M", "MM", "MMM", or "MMMM".')); + } + } + + /** + * Get the day of the month. + * "d" for non-padding, "dd" will always return 2 digits day numbers, e.g. 05. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string day of the month + */ + protected function formatDay($pattern,$date) + { + $day=$date['mday']; + if($pattern==='d') + return $day; + else if($pattern==='dd') + return str_pad($day,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for day of the month must be "d" or "dd".')); + } + + /** + * Get the day in the year, e.g. [1-366] + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return int hours in AM/PM format. + */ + protected function formatDayInYear($pattern,$date) + { + $day=$date['yday']; + if(($n=strlen($pattern))<=3) + return str_pad($day,$n,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for day in year must be "D", "DD" or "DDD".')); + } + + /** + * Get day of week in the month, e.g. 2nd Wed in July. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return int day in month + */ + protected function formatDayInMonth($pattern,$date) + { + if($pattern==='F') + return (int)(($date['mday']+6)/7); + else + throw new CException(Yii::t('yii','The pattern for day in month must be "F".')); + } + + /** + * Get the day of the week. + * "E", "EE", "EEE" will return abbreviated week day name, e.g. "Tues"; + * "EEEE" will return full week day name; + * "EEEEE" will return the narrow week day name, e.g. "T"; + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string day of the week. + */ + protected function formatDayInWeek($pattern,$date) + { + $day=$date['wday']; + switch($pattern) + { + case 'E': + case 'EE': + case 'EEE': + return $this->_locale->getWeekDayName($day,'abbreviated'); + case 'EEEE': + return $this->_locale->getWeekDayName($day,'wide'); + case 'EEEEE': + return $this->_locale->getWeekDayName($day,'narrow'); + default: + throw new CException(Yii::t('yii','The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".')); + } + } + + /** + * Get the AM/PM designator, 12 noon is PM, 12 midnight is AM. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string AM or PM designator + */ + protected function formatPeriod($pattern,$date) + { + if($pattern==='a') + { + if(intval($date['hours']/12)) + return $this->_locale->getPMName(); + else + return $this->_locale->getAMName(); + } + else + throw new CException(Yii::t('yii','The pattern for AM/PM marker must be "a".')); + } + + /** + * Get the hours in 24 hour format, i.e. [0-23]. + * "H" for non-padding, "HH" will always return 2 characters. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string hours in 24 hour format. + */ + protected function formatHour24($pattern,$date) + { + $hour=$date['hours']; + if($pattern==='H') + return $hour; + else if($pattern==='HH') + return str_pad($hour,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for 24 hour format must be "H" or "HH".')); + } + + /** + * Get the hours in 12 hour format, i.e., [1-12] + * "h" for non-padding, "hh" will always return 2 characters. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string hours in 12 hour format. + */ + protected function formatHour12($pattern,$date) + { + $hour=$date['hours']; + $hour=($hour==12|$hour==0)?12:($hour)%12; + if($pattern==='h') + return $hour; + else if($pattern==='hh') + return str_pad($hour,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for 12 hour format must be "h" or "hh".')); + } + + /** + * Get the hours [1-24]. + * 'k' for non-padding, and 'kk' with 2 characters padding. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return int hours [1-24] + */ + protected function formatHourInDay($pattern,$date) + { + $hour=$date['hours']==0?24:$date['hours']; + if($pattern==='k') + return $hour; + else if($pattern==='kk') + return str_pad($hour,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for hour in day must be "k" or "kk".')); + } + + /** + * Get the hours in AM/PM format, e.g [0-11] + * "K" for non-padding, "KK" will always return 2 characters. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return int hours in AM/PM format. + */ + protected function formatHourInPeriod($pattern,$date) + { + $hour=$date['hours']%12; + if($pattern==='K') + return $hour; + else if($pattern==='KK') + return str_pad($hour,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for hour in AM/PM must be "K" or "KK".')); + } + + /** + * Get the minutes. + * "m" for non-padding, "mm" will always return 2 characters. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string minutes. + */ + protected function formatMinutes($pattern,$date) + { + $minutes=$date['minutes']; + if($pattern==='m') + return $minutes; + else if($pattern==='mm') + return str_pad($minutes,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for minutes must be "m" or "mm".')); + } + + /** + * Get the seconds. + * "s" for non-padding, "ss" will always return 2 characters. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string seconds + */ + protected function formatSeconds($pattern,$date) + { + $seconds=$date['seconds']; + if($pattern==='s') + return $seconds; + else if($pattern==='ss') + return str_pad($seconds,2,'0',STR_PAD_LEFT); + else + throw new CException(Yii::t('yii','The pattern for seconds must be "s" or "ss".')); + } + + /** + * Get the week in the year. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return int week in year + */ + protected function formatWeekInYear($pattern,$date) + { + if($pattern==='w') + return @date('W',@mktime(0,0,0,$date['mon'],$date['mday'],$date['year'])); + else + throw new CException(Yii::t('yii','The pattern for week in year must be "w".')); + } + + /** + * Get week in the month. + * @param array result of {@link CTimestamp::getdate}. + * @param string a pattern. + * @return int week in month + */ + protected function formatWeekInMonth($pattern,$date) + { + if($pattern==='W') + return @date('W',@mktime(0,0,0,$date['mon'], $date['mday'],$date['year']))-date('W', mktime(0,0,0,$date['mon'],1,$date['year']))+1; + else + throw new CException(Yii::t('yii','The pattern for week in month must be "W".')); + } + + /** + * Get the timezone of the server machine. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string time zone + * @todo How to get the timezone for a different region? + */ + protected function formatTimeZone($pattern,$date) + { + if($pattern==='z' | $pattern==='v') + return @date('T', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year'])); + else + throw new CException(Yii::t('yii','The pattern for time zone must be "z" or "v".')); + } + + /** + * Get the era. i.e. in gregorian, year > 0 is AD, else BC. + * @param string a pattern. + * @param array result of {@link CTimestamp::getdate}. + * @return string era + * @todo How to support multiple Eras?, e.g. Japanese. + */ + protected function formatEra($pattern,$date) + { + $era=$date['year']>0 ? 1 : 0; + switch($pattern) + { + case 'G': + case 'GG': + case 'GGG': + return $this->_locale->getEraName($era,'abbreviated'); + case 'GGGG': + return $this->_locale->getEraName($era,'wide'); + case 'GGGGG': + return $this->_locale->getEraName($era,'narrow'); + default: + throw new CException(Yii::t('yii','The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".')); + } + } +} diff --git a/web/framework-1.0.8/i18n/CDbMessageSource.php b/web/framework-1.0.8/i18n/CDbMessageSource.php new file mode 100644 index 0000000..46e88ec --- /dev/null +++ b/web/framework-1.0.8/i18n/CDbMessageSource.php @@ -0,0 +1,114 @@ +<?php +/** + * CDbMessageSource class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbMessageSource represents a message source that stores translated messages in database. + * + * The database must contain the following two tables: + * <pre> + * CREATE TABLE SourceMessage + * ( + * id INTEGER PRIMARY KEY, + * category VARCHAR(32), + * message TEXT + * ); + * CREATE TABLE Message + * ( + * id INTEGER, + * language VARCHAR(16), + * translation TEXT, + * PRIMARY KEY (id, language), + * CONSTRAINT FK_Message_SourceMessage FOREIGN KEY (id) + * REFERENCES SourceMessage (id) ON DELETE CASCADE ON UPDATE RESTRICT + * ); + * </pre> + * The 'SourceMessage' table stores the messages to be translated, and the 'Message' table + * stores the translated messages. The name of these two tables can be customized by setting + * {@link sourceMessageTable} and {@link translatedMessageTable}, respectively. + * + * When {@link cachingDuration} is set as a positive number, message translations will be cached. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbMessageSource.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CDbMessageSource extends CMessageSource +{ + const CACHE_KEY_PREFIX='Yii.CDbMessageSource.'; + /** + * @var string the ID of the database connection application component. Defaults to 'db'. + */ + public $connectionID='db'; + /** + * @var string the name of the source message table. Defaults to 'SourceMessage'. + */ + public $sourceMessageTable='SourceMessage'; + /** + * @var string the name of the translated message table. Defaults to 'Message'. + */ + public $translatedMessageTable='Message'; + /** + * @var integer the time in seconds that the messages can remain valid in cache. + * Defaults to 0, meaning the caching is disabled. + */ + public $cachingDuration=0; + + private $_db; + + /** + * Initializes the application component. + * This method overrides the parent implementation by preprocessing + * the user request data. + */ + public function init() + { + parent::init(); + if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) + $this->_db->setActive(true); + else + throw new CException(Yii::t('yii','CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.', + array('{id}'=>$this->connectionID))); + } + + /** + * Loads the message translation for the specified language and category. + * @param string the message category + * @param string the target language + * @return array the loaded messages + */ + protected function loadMessages($category,$language) + { + if($this->cachingDuration>0 && ($cache=Yii::app()->getCache())!==null) + { + $key=self::CACHE_KEY_PREFIX.'.messages'; + if(($data=$cache->get($key))!==false) + return unserialize($data); + } + + $sql=<<<EOD +SELECT t1.message AS message, t2.translation AS translation +FROM {$this->sourceMessageTable} t1, {$this->translatedMessageTable} t2 +WHERE t1.id=t2.id AND t1.category=:category AND t2.language=:language +EOD; + $command=$this->_db->createCommand($sql); + $command->bindValue(':category',$category); + $command->bindValue(':language',$language); + $rows=$command->queryAll(); + $messages=array(); + foreach($rows as $row) + $messages[$row['message']]=$row['translation']; + + if(isset($cache)) + $cache->set($key,serialize($messages),$this->cachingDuration); + + return $messages; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/i18n/CGettextMessageSource.php b/web/framework-1.0.8/i18n/CGettextMessageSource.php new file mode 100644 index 0000000..97b6aeb --- /dev/null +++ b/web/framework-1.0.8/i18n/CGettextMessageSource.php @@ -0,0 +1,111 @@ +<?php +/** + * CGettextMessageSource class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGettextMessageSource represents a message source that is based on GNU Gettext. + * + * Each CGettextMessageSource instance represents the message tranlations + * for a single domain. And each message category represents a message context + * in Gettext. Translated messages are stored as either a MO or PO file, + * depending on the {@link useMoFile} property value. + * + * All translations are saved under the {@link basePath} directory. + * Translations in one language are kept as MO or PO files under an individual + * subdirectory whose name is the language ID. The file name is specified via + * {@link catalog} property, which defaults to 'messages'. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CGettextMessageSource.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CGettextMessageSource extends CMessageSource +{ + const CACHE_KEY_PREFIX='Yii.CGettextMessageSource.'; + const MO_FILE_EXT='.mo'; + const PO_FILE_EXT='.po'; + + /** + * @var integer the time in seconds that the messages can remain valid in cache. + * Defaults to 0, meaning the caching is disabled. + */ + public $cachingDuration=0; + /** + * @var string the base path for all translated messages. Defaults to null, meaning + * the "messages" subdirectory of the application directory (e.g. "protected/messages"). + */ + public $basePath; + /** + * @var boolean whether to load messages from MO files. Defaults to true. + * If false, messages will be loaded from PO files. + */ + public $useMoFile=true; + /** + * @var boolean whether to use Big Endian to read and write MO files. + * Defaults to false. This property is only used when {@link useMoFile} is true. + */ + public $useBigEndian=false; + /** + * @var string the message catalog name. This is the name of the message file (without extension) + * that stores the translated messages. Defaults to 'messages'. + */ + public $catalog='messages'; + + /** + * Initializes the application component. + * This method overrides the parent implementation by preprocessing + * the user request data. + */ + public function init() + { + parent::init(); + if($this->basePath===null) + $this->basePath=Yii::getPathOfAlias('application.messages'); + } + + /** + * Loads the message translation for the specified language and category. + * @param string the message category + * @param string the target language + * @return array the loaded messages + */ + protected function loadMessages($category, $language) + { + $messageFile=$this->basePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . $this->catalog; + if($this->useMoFile) + $messageFile.=self::MO_FILE_EXT; + else + $messageFile.=self::PO_FILE_EXT; + + if ($this->cachingDuration > 0 && ($cache=Yii::app()->getCache()) !== null) + { + $key = self::CACHE_KEY_PREFIX . $messageFile; + if (($data=$cache->get($key)) !== false) + return unserialize($data); + } + + if (is_file($messageFile)) + { + if($this->useMoFile) + $file=new CGettextMoFile($this->useBigEndian); + else + $file=new CGettextPoFile(); + $messages=$file->load($messageFile,$category); + if(isset($cache)) + { + $dependency=new CFileCacheDependency($messageFile); + $cache->set($key,serialize($messages),$this->cachingDuration,$dependency); + } + return $messages; + } + else + return array(); + } +} diff --git a/web/framework-1.0.8/i18n/CLocale.php b/web/framework-1.0.8/i18n/CLocale.php new file mode 100644 index 0000000..e854e47 --- /dev/null +++ b/web/framework-1.0.8/i18n/CLocale.php @@ -0,0 +1,243 @@ +<?php +/** + * CLocale class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLocale represents the data relevant to a locale. + * + * The data includes the number formatting information and date formatting information. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CLocale.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CLocale extends CComponent +{ + private $_id; + private $_data; + private $_dateFormatter; + private $_numberFormatter; + + /** + * Returns the instance of the specified locale. + * Since the constructor of CLocale is protected, you can only use + * this method to obtain an instance of the specified locale. + * @param string the locale ID (e.g. en_US) + * @return CLocale the locale instance + */ + public static function getInstance($id) + { + static $locales=array(); + if(isset($locales[$id])) + return $locales[$id]; + else + return $locales[$id]=new CLocale($id); + } + + /** + * @return array IDs of the locales which the framework can recognize + */ + public static function getLocaleIDs() + { + static $locales; + if($locales===null) + { + $locales=array(); + $dataPath=dirname(__FILE__).DIRECTORY_SEPARATOR.'data'; + $folder=@opendir($dataPath); + while($file=@readdir($folder)) + { + $fullPath=$dataPath.DIRECTORY_SEPARATOR.$file; + if(substr($file,-4)==='.php' && is_file($fullPath)) + $locales[]=substr($file,0,-4); + } + closedir($folder); + sort($locales); + } + return $locales; + } + + /** + * Constructor. + * Since the constructor is protected, please use {@link getInstance} + * to obtain an instance of the specified locale. + * @param string the locale ID (e.g. en_US) + */ + protected function __construct($id) + { + $this->_id=self::getCanonicalID($id); + $dataFile=dirname(__FILE__).DIRECTORY_SEPARATOR.'data'.DIRECTORY_SEPARATOR.$this->_id.'.php'; + if(is_file($dataFile)) + $this->_data=require($dataFile); + else + throw new CException(Yii::t('yii','Unrecognized locale "{locale}".',array('{locale}'=>$id))); + } + + /** + * Converts a locale ID to its canonical form. + * In canonical form, a locale ID consists of only underscores and lower-case letters. + * @param string the locale ID to be converted + * @return string the locale ID in canonical form + */ + public static function getCanonicalID($id) + { + return strtolower(str_replace('-','_',$id)); + } + + /** + * @return string the locale ID (in canonical form) + */ + public function getId() + { + return $this->_id; + } + + /** + * @return CNumberFormatter the number formatter for this locale + */ + public function getNumberFormatter() + { + if($this->_numberFormatter===null) + $this->_numberFormatter=new CNumberFormatter($this); + return $this->_numberFormatter; + } + + /** + * @return CDateFormatter the date formatter for this locale + */ + public function getDateFormatter() + { + if($this->_dateFormatter===null) + $this->_dateFormatter=new CDateFormatter($this); + return $this->_dateFormatter; + } + + /** + * @param string 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency. + * @return string the localized currency symbol. Null if the symbol does not exist. + */ + public function getCurrencySymbol($currency) + { + return isset($this->_data['currencySymbols'][$currency]) ? $this->_data['currencySymbols'][$currency] : null; + } + + /** + * @param string symbol name + * @return string symbol + */ + public function getNumberSymbol($name) + { + return isset($this->_data['numberSymbols'][$name]) ? $this->_data['numberSymbols'][$name] : null; + } + + /** + * @return string the decimal format + */ + public function getDecimalFormat() + { + return $this->_data['decimalFormat']; + } + + /** + * @return string the currency format + */ + public function getCurrencyFormat() + { + return $this->_data['currencyFormat']; + } + + /** + * @return string the percent format + */ + public function getPercentFormat() + { + return $this->_data['percentFormat']; + } + + /** + * @return string the scientific format + */ + public function getScientificFormat() + { + return $this->_data['scientificFormat']; + } + + /** + * @param integer month (1-12) + * @param string month name width. It can be 'wide', 'abbreviated' or 'narrow'. + * @return string the month name + */ + public function getMonthName($month,$width='wide') + { + return $this->_data['monthNames'][$width][$month]; + } + + /** + * @param integer weekday (0-6) + * @param string weekday name width. It can be 'wide', 'abbreviated' or 'narrow'. + * @return string the weekday name + */ + public function getWeekDayName($day,$width='wide') + { + return $this->_data['weekDayNames'][$width][$day]; + } + + /** + * @param integer era (0,1) + * @param string era name width. It can be 'wide', 'abbreviated' or 'narrow'. + * @return string the era name + */ + public function getEraName($era,$width='wide') + { + return $this->_data['eraNames'][$width][$era]; + } + + /** + * @return string the AM name + */ + public function getAMName() + { + return $this->_data['amName']; + } + + /** + * @return string the PM name + */ + public function getPMName() + { + return $this->_data['pmName']; + } + + /** + * @param string date format width. It can be 'full', 'long', 'medium' or 'short'. + * @return string date format + */ + public function getDateFormat($width='medium') + { + return $this->_data['dateFormats'][$width]; + } + + /** + * @param string time format width. It can be 'full', 'long', 'medium' or 'short'. + * @return string date format + */ + public function getTimeFormat($width='medium') + { + return $this->_data['timeFormats'][$width]; + } + + /** + * @return string datetime format, i.e., the order of date and time. + */ + public function getDateTimeFormat() + { + return $this->_data['dateTimeFormat']; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/i18n/CMessageSource.php b/web/framework-1.0.8/i18n/CMessageSource.php new file mode 100644 index 0000000..ad6ba9f --- /dev/null +++ b/web/framework-1.0.8/i18n/CMessageSource.php @@ -0,0 +1,157 @@ +<?php +/** + * CMessageSource class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMessageSource is the base class for message translation repository classes. + * + * A message source is an application component that provides message internationalization (i18n). + * It stores messages translated in different languages and provides + * these translated versions when requested. + * + * A concrete class must implement {@link loadMessages} or override {@link translateMessage}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMessageSource.php 752 2009-02-26 12:46:13Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +abstract class CMessageSource extends CApplicationComponent +{ + private $_language; + private $_messages=array(); + + /** + * Loads the message translation for the specified language and category. + * @param string the message category + * @param string the target language + * @return array the loaded messages + */ + abstract protected function loadMessages($category,$language); + + /** + * @return string the language that the source messages are written in. + * Defaults to {@link CApplication::language application language}. + */ + public function getLanguage() + { + return $this->_language===null ? Yii::app()->sourceLanguage : $this->_language; + } + + /** + * @param string the language that the source messages are written in. + */ + public function setLanguage($language) + { + $this->_language=CLocale::getCanonicalID($language); + } + + /** + * Translates a message to the specified language. + * + * Note, if the specified language is the same as + * the {@link getLanguage source message language}, messages will NOT be translated. + * + * If the message is not found in the translations, an {@link onMissingTranslation} + * event will be raised. Handlers can mark this message or do some + * default handling. The {@link CMissingTranslationEvent::message} + * property of the event parameter will be returned. + * + * @param string the message category + * @param string the message to be translated + * @param string the target language. If null (default), the {@link CApplication::getLanguage application language} will be used. + * This parameter has been available since version 1.0.3. + * @return string the translated message (or the original message if translation is not needed) + */ + public function translate($category,$message,$language=null) + { + if($language===null) + $language=Yii::app()->getLanguage(); + if($language!==$this->getLanguage()) + return $this->translateMessage($category,$message,$language); + else + return $message; + } + + /** + * Translates the specified message. + * If the message is not found, an {@link onMissingTranslation} + * event will be raised. + * @param string the category that the message belongs to + * @param string the message to be translated + * @param string the target language + * @return string the translated message + */ + protected function translateMessage($category,$message,$language) + { + $key=$language.'.'.$category; + if(!isset($this->_messages[$key])) + $this->_messages[$key]=$this->loadMessages($category,$language); + if(isset($this->_messages[$key][$message]) && $this->_messages[$key][$message]!=='') + return $this->_messages[$key][$message]; + else + { + $event=new CMissingTranslationEvent($this,$category,$message,$language); + $this->onMissingTranslation($event); + return $event->message; + } + } + + /** + * Raised when a message cannot be translated. + * Handlers may log this message or do some default handling. + * The {@link CMissingTranslationEvent::message} property + * will be returned by {@link translateMessage}. + * @param CMissingTranslationEvent the event parameter + */ + public function onMissingTranslation($event) + { + $this->raiseEvent('onMissingTranslation',$event); + } +} + + +/** + * CMissingTranslationEvent represents the parameter for the {@link CMessageSource::onMissingTranslation onMissingTranslation} event. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMessageSource.php 752 2009-02-26 12:46:13Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CMissingTranslationEvent extends CEvent +{ + /** + * @var string the message to be translated + */ + public $message; + /** + * @var string the category that the message belongs to + */ + public $category; + /** + * @var string the ID of the language that the message is to be translated to + */ + public $language; + + /** + * Constructor. + * @param mixed sender of this event + * @param string the category that the message belongs to + * @param string the message to be translated + * @param string the ID of the language that the message is to be translated to + */ + public function __construct($sender,$category,$message,$language) + { + parent::__construct($sender); + $this->message=$message; + $this->category=$category; + $this->language=$language; + } +} diff --git a/web/framework-1.0.8/i18n/CNumberFormatter.php b/web/framework-1.0.8/i18n/CNumberFormatter.php new file mode 100644 index 0000000..3a6cef6 --- /dev/null +++ b/web/framework-1.0.8/i18n/CNumberFormatter.php @@ -0,0 +1,280 @@ +<?php +/** + * CNumberFormatter class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CNumberFormatter provides number localization functionalities. + * + * CNumberFormatter formats a number (integer or double) and outputs a string + * based on the specified format. A CNumberFormatter instance is associated with a locale, + * and thus generates the string representation of the number in a locale-dependent fashion. + * + * CNumberFormatter currently supports currency format, percentage format, decimal format, + * and custom format. The first three formats are specified in the locale data, while the custom + * format allows you to enter an arbitrary format string. + * + * A format string may consist of the following special characters: + * <ul> + * <li>dot (.): the decimal point. It will be replaced with the localized decimal point.</li> + * <li>comma (,): the grouping separator. It will be replaced with the localized grouping separator.</li> + * <li>zero (0): required digit. This specifies the places where a digit must appear (will pad 0 if not).</li> + * <li>hash (#): optional digit. This is mainly used to specify the location of decimal point and grouping separators.</li> + * <li>currency (¤): the currency placeholder. It will be replaced with the localized currency symbol.</li> + * <li>percentage (%): the percetage mark. If appearing, the number will be multiplied by 100 before being formatted.</li> + * <li>permillage (‰): the permillage mark. If appearing, the number will be multiplied by 1000 before being formatted.</li> + * <li>semicolon (;): the character separating positive and negative number sub-patterns.</li> + * </ul> + * + * Anything surrounding the pattern (or sub-patterns) will be kept. + * + * The followings are some examples: + * <pre> + * Pattern "#,##0.00" will format 12345.678 as "12,345.68". + * Pattern "#,#,#0.00" will format 12345.6 as "1,2,3,45.60". + * </pre> + * Note, in the first example, the number is rounded first before applying the formatting. + * And in the second example, the pattern specifies two grouping sizes. + * + * CNumberFormatter attempts to implement number formatting according to + * the {@link http://www.unicode.org/reports/tr35/ Unicode Technical Standard #35}. + * The following features are NOT implemented: + * <ul> + * <li>significant digit</li> + * <li>scientific format</li> + * <li>arbitrary literal characters</li> + * <li>arbitrary padding</li> + * </ul> + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CNumberFormatter.php 1262 2009-07-21 19:00:55Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CNumberFormatter extends CComponent +{ + private $_locale; + private $_formats=array(); + + /** + * Constructor. + * @param mixed locale ID (string) or CLocale instance + */ + public function __construct($locale) + { + if(is_string($locale)) + $this->_locale=CLocale::getInstance($locale); + else + $this->_locale=$locale; + } + + /** + * Formats a number based on the specified pattern. + * Note, if the format contains '%', the number will be multiplied by 100 first. + * If the format contains '‰', the number will be multiplied by 1000. + * If the format contains currency placeholder, it will be replaced by + * the specified localized currency symbol. + * @param string format pattern + * @param mixed the number to be formatted + * @param string 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency. + * The currency placeholder in the pattern will be replaced with the currency symbol. + * If null, no replacement will be done. + * @return string the formatting result. + */ + public function format($pattern,$value,$currency=null) + { + $format=$this->parseFormat($pattern); + $result=$this->formatNumber($format,$value); + if($currency===null) + return $result; + else if(($symbol=$this->_locale->getCurrencySymbol($currency))===null) + $symbol=$currency; + return str_replace('¤',$symbol,$result); + } + + /** + * Formats a number using the currency format defined in the locale. + * @param mixed the number to be formatted + * @param string 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency. + * The currency placeholder in the pattern will be replaced with the currency symbol. + * @return string the formatting result. + */ + public function formatCurrency($value,$currency) + { + return $this->format($this->_locale->getCurrencyFormat(),$value,$currency); + } + + /** + * Formats a number using the percentage format defined in the locale. + * Note, if the percentage format contains '%', the number will be multiplied by 100 first. + * If the percentage format contains '‰', the number will be multiplied by 1000. + * @param mixed the number to be formatted + * @return string the formatting result. + */ + public function formatPercentage($value) + { + return $this->format($this->_locale->getPercentFormat(),$value); + } + + /** + * Formats a number using the decimal format defined in the locale. + * @param mixed the number to be formatted + * @return string the formatting result. + */ + public function formatDecimal($value) + { + return $this->format($this->_locale->getDecimalFormat(),$value); + } + + /** + * Formats a number based on a format. + * This is the method that does actual number formatting. + * @param array format with the following structure: + * <pre> + * array( + * 'decimalDigits'=>2, // number of required digits after decimal point; 0s will be padded if not enough digits; if -1, it means we should drop decimal point + * 'maxDecimalDigits'=>3, // maximum number of digits after decimal point. Additional digits will be truncated. + * 'integerDigits'=>1, // number of required digits before decimal point; 0s will be padded if not enough digits + * 'groupSize1'=>3, // the primary grouping size; if 0, it means no grouping + * 'groupSize2'=>0, // the secondary grouping size; if 0, it means no secondary grouping + * 'positivePrefix'=>'+', // prefix to positive number + * 'positiveSuffix'=>'', // suffix to positive number + * 'negativePrefix'=>'(', // prefix to negative number + * 'negativeSuffix'=>')', // suffix to negative number + * 'multiplier'=>1, // 100 for percent, 1000 for per mille + * ); + * </pre> + * @param mixed the number to be formatted + * @return string the formatted result + */ + protected function formatNumber($format,$value) + { + $negative=$value<0; + $value=abs($value*$format['multiplier']); + if($format['maxDecimalDigits']>=0) + $value=round($value,$format['maxDecimalDigits']); + $value="$value"; + if(($pos=strpos($value,'.'))!==false) + { + $integer=substr($value,0,$pos); + $decimal=substr($value,$pos+1); + } + else + { + $integer=$value; + $decimal=''; + } + + if($format['decimalDigits']>strlen($decimal)) + $decimal=str_pad($decimal,$format['decimalDigits'],'0'); + if(strlen($decimal)>0) + $decimal=$this->_locale->getNumberSymbol('decimal').$decimal; + + $integer=str_pad($integer,$format['integerDigits'],'0',STR_PAD_LEFT); + if($format['groupSize1']>0 && strlen($integer)>$format['groupSize1']) + { + $str1=substr($integer,0,-$format['groupSize1']); + $str2=substr($integer,-$format['groupSize1']); + $size=$format['groupSize2']>0?$format['groupSize2']:$format['groupSize1']; + $str1=str_pad($str1,(int)((strlen($str1)+$size-1)/$size)*$size,' ',STR_PAD_LEFT); + $integer=ltrim(implode($this->_locale->getNumberSymbol('group'),str_split($str1,$size))).$this->_locale->getNumberSymbol('group').$str2; + } + + if($negative) + $number=$format['negativePrefix'].$integer.$decimal.$format['negativeSuffix']; + else + $number=$format['positivePrefix'].$integer.$decimal.$format['positiveSuffix']; + + return strtr($number,array('%'=>$this->_locale->getNumberSymbol('percentSign'),'‰'=>$this->_locale->getNumberSymbol('perMille'))); + } + + /** + * Parses a given string pattern. + * @param string the pattern to be parsed + * @return array the parsed pattern + * @see formatNumber + */ + protected function parseFormat($pattern) + { + if(isset($this->_formats[$pattern])) + return $this->_formats[$pattern]; + + $format=array(); + + // find out prefix and suffix for positive and negative patterns + $patterns=explode(';',$pattern); + $format['positivePrefix']=$format['positiveSuffix']=$format['negativePrefix']=$format['negativeSuffix']=''; + if(preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[0],$matches)) + { + $format['positivePrefix']=$matches[1]; + $format['positiveSuffix']=$matches[2]; + } + + if(isset($patterns[1]) && preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[1],$matches)) // with a negative pattern + { + $format['negativePrefix']=$matches[1]; + $format['negativeSuffix']=$matches[2]; + } + else + { + $format['negativePrefix']=$this->_locale->getNumberSymbol('minusSign').$format['positivePrefix']; + $format['negativeSuffix']=$format['positiveSuffix']; + } + $pat=$patterns[0]; + + // find out multiplier + if(strpos($pat,'%')!==false) + $format['multiplier']=100; + else if(strpos($pat,'‰')!==false) + $format['multiplier']=1000; + else + $format['multiplier']=1; + + // find out things about decimal part + if(($pos=strpos($pat,'.'))!==false) + { + if(($pos2=strrpos($pat,'0'))>$pos) + $format['decimalDigits']=$pos2-$pos; + else + $format['decimalDigits']=0; + if(($pos3=strrpos($pat,'#'))>=$pos2) + $format['maxDecimalDigits']=$pos3-$pos; + else + $format['maxDecimalDigits']=$format['decimalDigits']; + $pat=substr($pat,0,$pos); + } + else // no decimal part + { + $format['decimalDigits']=0; + $format['maxDecimalDigits']=0; + } + + // find out things about integer part + $p=str_replace(',','',$pat); + if(($pos=strpos($p,'0'))!==false) + $format['integerDigits']=strrpos($p,'0')-$pos+1; + else + $format['integerDigits']=0; + // find out group sizes. some patterns may have two different group sizes + $p=str_replace('#','0',$pat); + if(($pos=strrpos($pat,','))!==false) + { + $format['groupSize1']=strrpos($p,'0')-$pos; + if(($pos2=strrpos(substr($p,0,$pos),','))!==false) + $format['groupSize2']=$pos-$pos2-1; + else + $format['groupSize2']=0; + } + else + $format['groupSize1']=$format['groupSize2']=0; + + return $this->_formats[$pattern]=$format; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/i18n/CPhpMessageSource.php b/web/framework-1.0.8/i18n/CPhpMessageSource.php new file mode 100644 index 0000000..a2eabe0 --- /dev/null +++ b/web/framework-1.0.8/i18n/CPhpMessageSource.php @@ -0,0 +1,96 @@ +<?php +/** + * CPhpMessageSource class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPhpMessageSource represents a message source that stores translated messages in PHP scripts. + * + * CPhpMessageSource uses PHP files and arrays to keep message translations. + * <ul> + * <li>All translations are saved under the {@link basePath} directory.</li> + * <li>Translations in one language are kept as PHP files under an individual subdirectory + * whose name is the same as the language ID. Each PHP file contains messages + * belonging to the same category, and the file name is the same as the category name.</li> + * <li>Within a PHP file, an array of (source, translation) pairs is returned. + * For example: + * <pre> + * return array( + * 'original message 1' => 'translated message 1', + * 'original message 2' => 'translated message 2', + * ); + * </pre> + * </li> + * </ul> + * When {@link cachingDuration} is set as a positive number, message translations will be cached. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CPhpMessageSource.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n + * @since 1.0 + */ +class CPhpMessageSource extends CMessageSource +{ + const CACHE_KEY_PREFIX='Yii.CPhpMessageSource.'; + + /** + * @var integer the time in seconds that the messages can remain valid in cache. + * Defaults to 0, meaning the caching is disabled. + */ + public $cachingDuration=0; + /** + * @var string the base path for all translated messages. Defaults to null, meaning + * the "messages" subdirectory of the application directory (e.g. "protected/messages"). + */ + public $basePath; + + /** + * Initializes the application component. + * This method overrides the parent implementation by preprocessing + * the user request data. + */ + public function init() + { + parent::init(); + if($this->basePath===null) + $this->basePath=Yii::getPathOfAlias('application.messages'); + } + + /** + * Loads the message translation for the specified language and category. + * @param string the message category + * @param string the target language + * @return array the loaded messages + */ + protected function loadMessages($category,$language) + { + $messageFile=$this->basePath.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.$category.'.php'; + + if($this->cachingDuration>0 && ($cache=Yii::app()->getCache())!==null) + { + $key=self::CACHE_KEY_PREFIX . $messageFile; + if(($data=$cache->get($key))!==false) + return unserialize($data); + } + + if(is_file($messageFile)) + { + $messages=include($messageFile); + if(!is_array($messages)) + $messages=array(); + if(isset($cache)) + { + $dependency=new CFileCacheDependency($messageFile); + $cache->set($key,serialize($messages),$this->cachingDuration,$dependency); + } + return $messages; + } + else + return array(); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/i18n/gettext/CGettextFile.php b/web/framework-1.0.8/i18n/gettext/CGettextFile.php new file mode 100644 index 0000000..101a81a --- /dev/null +++ b/web/framework-1.0.8/i18n/gettext/CGettextFile.php @@ -0,0 +1,36 @@ +<?php +/** + * CGettextFile class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGettextFile is the base class for representing a Gettext message file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CGettextFile.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n.gettext + * @since 1.0 + */ +abstract class CGettextFile extends CComponent +{ + /** + * Loads messages from a file. + * @param string file path + * @param string message context + * @return array message translations (source message => translated message) + */ + abstract public function load($file,$context); + /** + * Saves messages to a file. + * @param string file path + * @param array message translations (message id => translated message). + * Note if the message has a context, the message id must be prefixed with + * the context with chr(4) as the separator. + */ + abstract public function save($file,$messages); +} diff --git a/web/framework-1.0.8/i18n/gettext/CGettextMoFile.php b/web/framework-1.0.8/i18n/gettext/CGettextMoFile.php new file mode 100644 index 0000000..0b205cb --- /dev/null +++ b/web/framework-1.0.8/i18n/gettext/CGettextMoFile.php @@ -0,0 +1,269 @@ +<?php +/** + * CGettextMoFile class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGettextMoFile represents an MO Gettext message file. + * + * This class is written by adapting Michael's Gettext_MO class in PEAR. + * Please refer to the following license terms. + * + * Copyright (c) 2004-2005, Michael Wallner <mike@iworks.at>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CGettextMoFile.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n.gettext + * @since 1.0 + */ +class CGettextMoFile extends CGettextFile +{ + /** + * @var boolean whether to use Big Endian when reading and writing an integer. + */ + public $useBigEndian=false; + + /** + * Constructor. + * @param boolean whether to use Big Endian when reading and writing an integer. + */ + public function __construct($useBigEndian=false) + { + $this->useBigEndian=$useBigEndian; + } + + /** + * Loads messages from an MO file. + * @param string file path + * @param string message context + * @return array message translations (source message => translated message) + */ + public function load($file,$context) + { + if(!($fr=@fopen($file,'rb'))) + throw new CException(Yii::t('yii','Unable to read file "{file}".', + array('{file}'=>$file))); + + if(!@flock($fr,LOCK_SH)) + throw new CException(Yii::t('yii','Unable to lock file "{file}" for reading.', + array('{file}'=>$file))); + + $magic=current($array=unpack('c',$this->readByte($fr,4))); + if($magic==-34) + $this->useBigEndian=false; + else if($magic==-107) + $this->useBigEndian=true; + else + throw new CException(Yii::t('yii','Invalid MO file: {file} (magic: {magic}).', + array('{file}'=>$file,'{magic}'=>$magic))); + + if(($revision=$this->readInteger($fr))!=0) + throw new CException(Yii::t('yii','Invalid MO file revision: {revision}.', + array('{revision}'=>$revision))); + + $count=$this->readInteger($fr); + $sourceOffset=$this->readInteger($fr); + $targetOffset=$this->readInteger($fr); + + $sourceLengths=array(); + $sourceOffsets=array(); + fseek($fr,$sourceOffset); + for($i=0;$i<$count;++$i) + { + $sourceLengths[]=$this->readInteger($fr); + $sourceOffsets[]=$this->readInteger($fr); + } + + $targetLengths=array(); + $targetOffsets=array(); + fseek($fr,$targetOffset); + for($i=0;$i<$count;++$i) + { + $targetLengths[]=$this->readInteger($fr); + $targetOffsets[]=$this->readInteger($fr); + } + + $messages=array(); + for($i=0;$i<$count;++$i) + { + $id=$this->readString($fr,$sourceLengths[$i],$sourceOffsets[$i]); + if(($pos=strpos($id,chr(4)))!==false && substr($id,0,$pos)===$context) + { + $id=substr($id,$pos+1); + $message=$this->readString($fr,$targetLengths[$i],$targetOffsets[$i]); + $messages[$id]=$message; + } + } + + @flock($fr,LOCK_UN); + @fclose($fr); + + return $messages; + } + + /** + * Saves messages to an MO file. + * @param string file path + * @param array message translations (message id => translated message). + * Note if the message has a context, the message id must be prefixed with + * the context with chr(4) as the separator. + */ + public function save($file,$messages) + { + if(!($fw=@fopen($file,'wb'))) + throw new CException(Yii::t('yii','Unable to write file "{file}".', + array('{file}'=>$file))); + + if(!@flock($fw,LOCK_EX)) + throw new CException(Yii::t('yii','Unable to lock file "{file}" for writing.', + array('{file}'=>$file))); + + // magic + if($this->useBigEndian) + $this->writeByte($fw,pack('c*', 0x95, 0x04, 0x12, 0xde)); + else + $this->writeByte($fw,pack('c*', 0xde, 0x12, 0x04, 0x95)); + + // revision + $this->writeInteger($fw,0); + + // message count + $n=count($messages); + $this->writeInteger($fw,$n); + + // offset of source message table + $offset=28; + $this->writeInteger($fw,$offset); + $offset+=($n*8); + $this->writeInteger($fw,$offset); + // hashtable size, omitted + $this->writeInteger($fw,0); + $offset+=($n*8); + $this->writeInteger($fw,$offset); + + // length and offsets for source messagess + foreach(array_keys($messages) as $id) + { + $len=strlen($id); + $this->writeInteger($fw,$len); + $this->writeInteger($fw,$offset); + $offset+=$len+1; + } + + // length and offsets for target messagess + foreach($messages as $message) + { + $len=strlen($message); + $this->writeInteger($fw,$len); + $this->writeInteger($fw,$offset); + $offset+=$len+1; + } + + // source messages + foreach(array_keys($messages) as $id) + $this->writeString($fw,$id); + + // target messages + foreach($messages as $message) + $this->writeString($fw,$message); + + @flock($fw,LOCK_UN); + @fclose($fw); + } + + /** + * Reads one or several bytes. + * @param resource file handle + * @param integer number of bytes to read + * @return string bytes + */ + protected function readByte($fr,$n=1) + { + if($n>0) + return fread($fr,$n); + } + + /** + * Writes bytes. + * @param resource file handle + * @param string the data + * @return integer how many bytes are written + */ + protected function writeByte($fw,$data) + { + return fwrite($fw,$data); + } + + /** + * Reads a 4-byte integer. + * @param resource file handle + * @return integer the result + * @see useBigEndian + */ + protected function readInteger($fr) + { + return current($array=unpack($this->useBigEndian ? 'N' : 'V', $this->readByte($fr,4))); + } + + /** + * Writes a 4-byte integer. + * @param resource file handle + * @param integer the data + * @return integer how many bytes are written + */ + protected function writeInteger($fw,$data) + { + return $this->writeByte($fw,pack($this->useBigEndian ? 'N' : 'V', (int)$data)); + } + + /** + * Reads a string. + * @param resource file handle + * @param integer string length + * @param integer offset of the string in the file. If null, it reads from the current position. + * @return string the result + */ + protected function readString($fr,$length,$offset=null) + { + if($offset!==null) + fseek($fr,$offset); + return $this->readByte($fr,$length); + } + + /** + * Writes a string. + * @param resource file handle + * @param string the string + * @return integer how many bytes are written + */ + protected function writeString($fw,$data) + { + return $this->writeByte($fw,$data."\0"); + } +} diff --git a/web/framework-1.0.8/i18n/gettext/CGettextPoFile.php b/web/framework-1.0.8/i18n/gettext/CGettextPoFile.php new file mode 100644 index 0000000..e52f877 --- /dev/null +++ b/web/framework-1.0.8/i18n/gettext/CGettextPoFile.php @@ -0,0 +1,89 @@ +<?php +/** + * CGettextPoFile class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGettextPoFile represents a PO Gettext message file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CGettextPoFile.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.i18n.gettext + * @since 1.0 + */ +class CGettextPoFile extends CGettextFile +{ + /** + * Loads messages from a PO file. + * @param string file path + * @param string message context + * @return array message translations (source message => translated message) + */ + public function load($file,$context) + { + $pattern='/msgctxt\s+"(.*?(?<!\\\\))"' + . '\s+msgid\s+"(.*?(?<!\\\\))"' + . '\s+msgstr\s+"(.*?(?<!\\\\))"/'; + $content=file_get_contents($file); + $n=preg_match_all($pattern,$content,$matches); + $messages=array(); + for($i=0;$i<$n;++$i) + { + if($matches[1][$i]===$context) + { + $id=$this->decode($matches[2][$i]); + $message=$this->decode($matches[3][$i]); + $messages[$id]=$message; + } + } + return $messages; + } + + /** + * Saves messages to a PO file. + * @param string file path + * @param array message translations (message id => translated message). + * Note if the message has a context, the message id must be prefixed with + * the context with chr(4) as the separator. + */ + public function save($file,$messages) + { + $content=''; + foreach($messages as $id=>$message) + { + if(($pos=strpos($id,chr(4)))!==false) + { + $content.='msgctxt "'.substr($id,0,$pos)."\"\n"; + $id=substr($id,$pos+1); + } + $content.='msgid "'.$this->encode($id)."\"\n"; + $content.='msgstr "'.$this->encode($message)."\"\n\n"; + } + file_put_contents($file,$content); + } + + /** + * Encodes special characters in a message. + * @param string message to be encoded + * @return string the encoded message + */ + protected function encode($string) + { + return str_replace(array('"', "\n", "\t", "\r"),array('\\"', "\\n", '\\t', '\\r'),$string); + } + + /** + * Decodes special characters in a message. + * @param string message to be decoded + * @return string the decoded message + */ + protected function decode($string) + { + return str_replace(array('\\"', "\\n", '\\t', '\\r'),array('"', "\n", "\t", "\r"),$string); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/logging/CDbLogRoute.php b/web/framework-1.0.8/logging/CDbLogRoute.php new file mode 100644 index 0000000..352cabd --- /dev/null +++ b/web/framework-1.0.8/logging/CDbLogRoute.php @@ -0,0 +1,168 @@ +<?php +/** + * CDbLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CDbLogRoute stores log messages in a database table. + * + * To specify the database table for storing log messages, set {@link logTableName} as + * the name of the table and specify {@link connectionID} to be the ID of a {@link CDbConnection} + * application component. If they are not set, a SQLite3 database named 'log-YiiVersion.db' will be created + * and used under the application runtime directory. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbLogRoute.php 1170 2009-06-25 13:30:53Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CDbLogRoute extends CLogRoute +{ + /** + * @var string the ID of CDbConnection application component. If not set, a SQLite database + * will be automatically created and used. The SQLite database file is + * <code>protected/runtime/log-YiiVersion.db</code>. + */ + public $connectionID; + /** + * @var string the name of the DB table that stores log content. Defaults to 'YiiLog'. + * If {@link autoCreateLogTable} is false and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + * <pre> + * ( + * id INTEGER NOT NULL PRIMARY KEY, + * level VARCHAR(128), + * category VARCHAR(128), + * logtime INTEGER, + * message TEXT + * ) + * </pre> + * Note, the 'id' column must be created as an auto-incremental column. + * In MySQL, this means it should be <code>id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY</code>; + * In PostgreSQL, it is <code>id SERIAL PRIMARY KEY</code>. + * @see autoCreateLogTable + */ + public $logTableName='YiiLog'; + /** + * @var boolean whether the log DB table should be automatically created if not exists. Defaults to true. + * @see logTableName + */ + public $autoCreateLogTable=true; + /** + * @var CDbConnection the DB connection instance + */ + private $_db; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->_db!==null) + $this->_db->setActive(false); + } + + /** + * Initializes the route. + * This method is invoked after the route is created by the route manager. + */ + public function init() + { + parent::init(); + + $db=$this->getDbConnection(); + $db->setActive(true); + + if($this->autoCreateLogTable) + { + $sql="DELETE FROM {$this->logTableName} WHERE 0=1"; + try + { + $db->createCommand($sql)->execute(); + } + catch(Exception $e) + { + $this->createLogTable($db,$this->logTableName); + } + } + } + + /** + * Creates the DB table for storing log messages. + * @param CDbConnection the database connection + * @param string the name of the table to be created + */ + protected function createLogTable($db,$tableName) + { + $driver=$db->getDriverName(); + if($driver==='mysql') + $logID='id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY'; + else if($driver==='pgsql') + $logID='id SERIAL PRIMARY KEY'; + else + $logID='id INTEGER NOT NULL PRIMARY KEY'; + + $sql=" +CREATE TABLE $tableName +( + $logID, + level VARCHAR(128), + category VARCHAR(128), + logtime INTEGER, + message TEXT +)"; + $db->createCommand($sql)->execute(); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($id=$this->connectionID)!==null) + { + if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.', + array('{id}'=>$id))); + } + else + { + $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'log-'.Yii::getVersion().'.db'; + return $this->_db=new CDbConnection('sqlite:'.$dbFile); + } + } + + /** + * Stores log messages into database. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $sql=" +INSERT INTO {$this->logTableName} +(level, category, logtime, message) VALUES +(:level, :category, :logtime, :message) +"; + $command=$this->getDbConnection()->createCommand($sql); + foreach($logs as $log) + { + $command->bindValue(':level',$log[1]); + $command->bindValue(':category',$log[2]); + $command->bindValue(':logtime',(int)$log[3]); + $command->bindValue(':message',$log[0]); + $command->execute(); + } + } +} diff --git a/web/framework-1.0.8/logging/CEmailLogRoute.php b/web/framework-1.0.8/logging/CEmailLogRoute.php new file mode 100644 index 0000000..2a782f5 --- /dev/null +++ b/web/framework-1.0.8/logging/CEmailLogRoute.php @@ -0,0 +1,122 @@ +<?php +/** + * CEmailLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CEmailLogRoute sends selected log messages to email addresses. + * + * The target email addresses may be specified via {@link setEmails emails} property. + * Optionally, you may set the email {@link setSubject subject} and the + * {@link setSentFrom sentFrom} address. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CEmailLogRoute.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CEmailLogRoute extends CLogRoute +{ + /** + * Default email subject. + */ + const DEFAULT_SUBJECT='Application Log'; + /** + * @var array list of destination email addresses. + */ + private $_email=array(); + /** + * @var string email subject + */ + private $_subject=''; + /** + * @var string email sent from address + */ + private $_from=''; + + /** + * Sends log messages to specified email addresses. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $message=''; + foreach($logs as $log) + $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]); + $message=wordwrap($message,70); + foreach($this->getEmails() as $email) + $this->sendEmail($email,$this->getSubject(),$message); + } + + /** + * Sends an email. + * @param string single email address + * @param string email subject + * @param string email content + */ + protected function sendEmail($email,$subject,$message) + { + if(($from=$this->getSentFrom())!=='') + mail($email,$subject,$message,"From:{$from}\r\n"); + else + mail($email,$subject,$message); + } + + /** + * @return array list of destination email addresses + */ + public function getEmails() + { + return $this->_email; + } + + /** + * @return array|string list of destination email addresses. If the value is + * a string, it is assumed to be comma-separated email addresses. + */ + public function setEmails($value) + { + if(is_array($value)) + $this->_email=$value; + else + $this->_email=preg_split('/[\s,]+/',$value,-1,PREG_SPLIT_NO_EMPTY); + } + + /** + * @return string email subject. Defaults to CEmailLogRoute::DEFAULT_SUBJECT + */ + public function getSubject() + { + return $this->_subject; + } + + /** + * @param string email subject. + */ + public function setSubject($value) + { + $this->_subject=$value; + } + + /** + * @return string send from address of the email + */ + public function getSentFrom() + { + return $this->_from; + } + + /** + * @param string send from address of the email + */ + public function setSentFrom($value) + { + $this->_from=$value; + } +} + diff --git a/web/framework-1.0.8/logging/CFileLogRoute.php b/web/framework-1.0.8/logging/CFileLogRoute.php new file mode 100644 index 0000000..b841ce5 --- /dev/null +++ b/web/framework-1.0.8/logging/CFileLogRoute.php @@ -0,0 +1,162 @@ +<?php +/** + * CFileLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileLogRoute records log messages in files. + * + * The log files are stored under {@link setLogPath logPath} and the file name + * is specified by {@link setLogFile logFile}. If the size of the log file is + * greater than {@link setMaxFileSize maxFileSize} (in kilo-bytes), a rotation + * is performed, which renames the current log file by suffixing the file name + * with '.1'. All existing log files are moved backwards one place, i.e., '.2' + * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles maxLogFiles} + * specifies how many files to be kept. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileLogRoute.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CFileLogRoute extends CLogRoute +{ + /** + * @var integer maximum log file size + */ + private $_maxFileSize=1024; // in KB + /** + * @var integer number of log files used for rotation + */ + private $_maxLogFiles=5; + /** + * @var string directory storing log files + */ + private $_logPath; + /** + * @var string log file name + */ + private $_logFile='application.log'; + + + /** + * Initializes the route. + * This method is invoked after the route is created by the route manager. + */ + public function init() + { + parent::init(); + if($this->getLogPath()===null) + $this->setLogPath(Yii::app()->getRuntimePath()); + } + + /** + * @return string directory storing log files. Defaults to application runtime path. + */ + public function getLogPath() + { + return $this->_logPath; + } + + /** + * @param string directory for storing log files. + * @throws CException if the path is invalid + */ + public function setLogPath($value) + { + $this->_logPath=realpath($value); + if($this->_logPath===false || !is_dir($this->_logPath) || !is_writable($this->_logPath)) + throw new CException(Yii::t('yii','CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.', + array('{path}'=>$value))); + } + + /** + * @return string log file name. Defaults to 'application.log'. + */ + public function getLogFile() + { + return $this->_logFile; + } + + /** + * @param string log file name + */ + public function setLogFile($value) + { + $this->_logFile=$value; + } + + /** + * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). + */ + public function getMaxFileSize() + { + return $this->_maxFileSize; + } + + /** + * @param integer maximum log file size in kilo-bytes (KB). + */ + public function setMaxFileSize($value) + { + if(($this->_maxFileSize=(int)$value)<1) + $this->_maxFileSize=1; + } + + /** + * @return integer number of files used for rotation. Defaults to 5. + */ + public function getMaxLogFiles() + { + return $this->_maxLogFiles; + } + + /** + * @param integer number of files used for rotation. + */ + public function setMaxLogFiles($value) + { + if(($this->_maxLogFiles=(int)$value)<1) + $this->_maxLogFiles=1; + } + + /** + * Saves log messages in files. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile(); + if(@filesize($logFile)>$this->getMaxFileSize()*1024) + $this->rotateFiles(); + foreach($logs as $log) + error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile); + } + + /** + * Rotates log files. + */ + protected function rotateFiles() + { + $file=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile(); + $max=$this->getMaxLogFiles(); + for($i=$max;$i>0;--$i) + { + $rotateFile=$file.'.'.$i; + if(is_file($rotateFile)) + { + if($i===$max) + unlink($rotateFile); + else + rename($rotateFile,$file.'.'.($i+1)); + } + } + if(is_file($file)) + rename($file,$file.'.1'); + } +} diff --git a/web/framework-1.0.8/logging/CLogFilter.php b/web/framework-1.0.8/logging/CLogFilter.php new file mode 100644 index 0000000..ca84416 --- /dev/null +++ b/web/framework-1.0.8/logging/CLogFilter.php @@ -0,0 +1,103 @@ +<?php +/** + * CLogFilter class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLogFilter preprocesses the logged messages before they are handled by a log route. + * + * CLogFilter is meant to be used by a log route to preprocess the logged messages + * before they are handled by the route. The default implementation of CLogFilter + * prepends additional context information to the logged messages. In particular, + * by setting {@link logVars}, predefined PHP variables such as + * $_SERVER, $_POST, etc. can be saved as a log message, which may help identify/debug + * issues encountered. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CLogFilter.php 1094 2009-06-05 14:55:14Z qiang.xue $ + * @package system.logging + * @since 1.0.6 + */ +class CLogFilter extends CComponent +{ + /** + * @var boolean whether to prefix each log message with the current user session ID. + * Defaults to false. + */ + public $prefixSession=false; + /** + * @var boolean whether to prefix each log message with the current user + * {@link CWebUser::name name} and {@link CWebUser::id ID}. Defaults to false. + */ + public $prefixUser=false; + /** + * @var boolean whether to log the current user name and ID. Defaults to true. + * This property is effective only when {@link showContext} is true. + */ + public $logUser=true; + /** + * @var array list of the PHP predefined variables that should be logged. + * Note that a variable must be accessible via $GLOBALS. Otherwise it won't be logged. + */ + public $logVars=array('_GET','_POST','_FILES','_COOKIE','_SESSION','_SERVER'); + + + /** + * Filters the given log messages. + * This is the main method of CLogFilter. It processes the log messages + * by adding context information, etc. + * @param array the log messages + */ + public function filter(&$logs) + { + if(($message=$this->getContext())!=='') + array_unshift($logs,array($message,CLogger::LEVEL_INFO,'application',YII_BEGIN_TIME)); + $this->format($logs); + return $logs; + } + + /** + * Formats the log messages. + * The default implementation will prefix each message with session ID + * if {@link prefixSession} is set true. It may also prefix each message + * with the current user's name and ID if {@link prefixUser} is true. + */ + protected function format(&$logs) + { + $prefix=''; + if($this->prefixSession && ($id=session_id())!=='') + $prefix.="[$id]"; + if($this->prefixUser && ($user=Yii::app()->getComponent('user',false))!==null) + $prefix.='['.$user->getName().']['.$user->getId().']'; + if($prefix!=='') + { + foreach($logs as &$log) + $log[0]=$prefix.' '.$log[0]; + } + } + + /** + * Generates the context information to be logged. + * The default implementation will dump user information, system variables, etc. + * @return string the context information. If an empty string, it means no context information. + */ + protected function getContext() + { + $context=array(); + if($this->logUser && ($user=Yii::app()->getComponent('user',false))!==null) + $context[]='User: '.$user->getName().' (ID: '.$user->getId().')'; + + foreach($this->logVars as $name) + { + if(isset($GLOBALS[$name]) && !empty($GLOBALS[$name])) + $context[]="\${$name}=".var_export($GLOBALS[$name],true); + } + + return implode("\n\n",$context); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/logging/CLogRoute.php b/web/framework-1.0.8/logging/CLogRoute.php new file mode 100644 index 0000000..7f0ca17 --- /dev/null +++ b/web/framework-1.0.8/logging/CLogRoute.php @@ -0,0 +1,107 @@ +<?php +/** + * CLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLogRoute is the base class for all log route classes. + * + * A log route object retrieves log messages from a logger and sends it + * somewhere, such as files, emails. + * The messages being retrieved may be filtered first before being sent + * to the destination. The filters include log level filter and log category filter. + * + * To specify level filter, set {@link levels} property, + * which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). + * To specify category filter, set {@link categories} property, + * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). + * + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CLogRoute.php 1165 2009-06-22 21:23:30Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +abstract class CLogRoute extends CComponent +{ + /** + * @var boolean whether to enable this log route. Defaults to true. + * @since 1.0.7 + */ + public $enabled=true; + /** + * @var string list of levels separated by comma or space. Defaults to empty, meaning all levels. + */ + public $levels=''; + /** + * @var string list of categories separated by comma or space. Defaults to empty, meaning all categories. + */ + public $categories=''; + /** + * @var mixed the additional filter (e.g. {@link CLogFilter}) that can be applied to the log messages. + * The value of this property will be passed to {@link Yii::createComponent} to create + * a log filter object. As a result, this can be either a string representing the + * filter class name or an array representing the filter configuration. + * In general, the log filter class should be {@link CLogFilter} or a child class of it. + * Defaults to null, meaning no filter will be used. + * @since 1.0.6 + */ + public $filter; + + + /** + * Initializes the route. + * This method is invoked after the route is created by the route manager. + */ + public function init() + { + } + + /** + * Formats a log message given different fields. + * @param string message content + * @param integer message level + * @param string message category + * @param integer timestamp + * @return string formatted message + */ + protected function formatLogMessage($message,$level,$category,$time) + { + return @date('Y/m/d H:i:s',$time)." [$level] [$category] $message\n"; + } + + /** + * Retrieves filtered log messages from logger for further processing. + * @param CLogger logger instance + */ + public function collectLogs($logger) + { + $logs=$logger->getLogs($this->levels,$this->categories); + if(!empty($logs)) + { + if($this->filter!==null) + Yii::createComponent($this->filter)->filter($logs); + $this->processLogs($logs); + } + } + + /** + * Processes log messages and sends them to specific destination. + * Derived child classes must implement this method. + * @param array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message (string) + * [1] => level (string) + * [2] => category (string) + * [3] => timestamp (float, obtained by microtime(true)); + */ + abstract protected function processLogs($logs); +} diff --git a/web/framework-1.0.8/logging/CLogRouter.php b/web/framework-1.0.8/logging/CLogRouter.php new file mode 100644 index 0000000..dd6b117 --- /dev/null +++ b/web/framework-1.0.8/logging/CLogRouter.php @@ -0,0 +1,107 @@ +<?php +/** + * CLogRouter class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLogRouter manages log routes that record log messages in different media. + * + * For example, a file log route {@link CFileLogRoute} records log messages + * in log files. An email log route {@link CEmailLogRoute} sends log messages + * to specific email addresses. See {@link CLogRoute} for more details about + * different log routes. + * + * Log routes may be configured in application configuration like following: + * <pre> + * array( + * 'preload'=>array('log'), // preload log component when app starts + * 'components'=>array( + * 'log'=>array( + * 'class'=>'CLogRouter', + * 'routes'=>array( + * array( + * 'class'=>'CFileLogRoute', + * 'levels'=>'trace, info', + * 'categories'=>'system.*', + * ), + * array( + * 'class'=>'CEmailLogRoute', + * 'levels'=>'error, warning', + * 'email'=>'admin@example.com', + * ), + * ), + * ), + * ), + * ) + * </pre> + * + * You can specify multiple routes with different filtering conditions and different + * targets, even if the routes are of the same type. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CLogRouter.php 1184 2009-06-28 00:30:10Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CLogRouter extends CApplicationComponent +{ + private $_routes=array(); + + /** + * Initializes this application component. + * This method is required by the IApplicationComponent interface. + */ + public function init() + { + parent::init(); + foreach($this->_routes as $name=>$route) + { + $route=Yii::createComponent($route); + $route->init(); + $this->_routes[$name]=$route; + } + Yii::app()->attachEventHandler('onEndRequest',array($this,'collectLogs')); + } + + /** + * @return array the currently initialized routes + */ + public function getRoutes() + { + return new CMap($this->_routes); + } + + /** + * @param array list of route configurations. Each array element represents + * the configuration for a single route and has the following array structure: + * <ul> + * <li>class: specifies the class name or alias for the route class.</li> + * <li>name-value pairs: configure the initial property values of the route.</li> + * </ul> + */ + public function setRoutes($config) + { + foreach($config as $name=>$route) + $this->_routes[$name]=$route; + } + + /** + * Collects log messages from a logger. + * This method is an event handler to application's onEndRequest event. + * @param mixed event parameter + */ + public function collectLogs($param) + { + $logger=Yii::getLogger(); + foreach($this->_routes as $route) + { + if($route->enabled) + $route->collectLogs($logger); + } + } +} diff --git a/web/framework-1.0.8/logging/CLogger.php b/web/framework-1.0.8/logging/CLogger.php new file mode 100644 index 0000000..8d1a92f --- /dev/null +++ b/web/framework-1.0.8/logging/CLogger.php @@ -0,0 +1,237 @@ +<?php +/** + * CLogger class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLogger records log messages in memory. + * + * CLogger implements the methods to retrieve the messages with + * various filter conditions, including log levels and log categories. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CLogger.php 1042 2009-05-21 19:53:47Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CLogger extends CComponent +{ + const LEVEL_TRACE='trace'; + const LEVEL_WARNING='warning'; + const LEVEL_ERROR='error'; + const LEVEL_INFO='info'; + const LEVEL_PROFILE='profile'; + + /** + * @var array log messages + */ + private $_logs=array(); + /** + * @var array log levels for filtering (used when filtering) + */ + private $_levels; + /** + * @var array log categories for filtering (used when filtering) + */ + private $_categories; + /** + * @var array the profiling results (category, token => time in seconds) + * @since 1.0.6 + */ + private $_timings; + + /** + * Logs a message. + * Messages logged by this method may be retrieved back via {@link getLogs}. + * @param string message to be logged + * @param string level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive. + * @param string category of the message (e.g. 'system.web'). It is case-insensitive. + * @see getLogs + */ + public function log($message,$level='info',$category='application') + { + $this->_logs[]=array($message,$level,$category,microtime(true)); + } + + /** + * Retrieves log messages. + * + * Messages may be filtered by log levels and/or categories. + * A level filter is specified by a list of levels separated by comma or space + * (e.g. 'trace, error'). A category filter is similar to level filter + * (e.g. 'system, system.web'). A difference is that in category filter + * you can use pattern like 'system.*' to indicate all categories starting + * with 'system'. + * + * If you do not specify level filter, it will bring back logs at all levels. + * The same applies to category filter. + * + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will be returned. + * + * @param string level filter + * @param string category filter + * @return array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message (string) + * [1] => level (string) + * [2] => category (string) + * [3] => timestamp (float, obtained by microtime(true)); + */ + public function getLogs($levels='',$categories='') + { + $this->_levels=preg_split('/[\s,]+/',strtolower($levels),-1,PREG_SPLIT_NO_EMPTY); + $this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY); + if(empty($levels) && empty($categories)) + return $this->_logs; + else if(empty($levels)) + return array_values(array_filter(array_filter($this->_logs,array($this,'filterByCategory')))); + else if(empty($categories)) + return array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevel')))); + else + { + $ret=array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevel')))); + return array_values(array_filter(array_filter($ret,array($this,'filterByCategory')))); + } + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + * @return array valid log, false if not. + */ + private function filterByCategory($value) + { + foreach($this->_categories as $category) + { + $cat=strtolower($value[2]); + if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0)) + return $value; + } + return false; + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + * @return array valid log, false if not. + */ + private function filterByLevel($value) + { + return in_array(strtolower($value[1]),$this->_levels)?$value:false; + } + + /** + * Returns the total time for serving the current request. + * This method calculates the difference between now and the timestamp + * defined by constant YII_BEGIN_TIME. + * To estimate the execution time more accurately, the constant should + * be defined as early as possible (best at the beginning of the entry script.) + * @return float the total time for serving the current request. + */ + public function getExecutionTime() + { + return microtime(true)-YII_BEGIN_TIME; + } + + /** + * Returns the memory usage of the current application. + * This method relies on the PHP function memory_get_usage(). + * If it is not available, the method will attempt to use OS programs + * to determine the memory usage. A value 0 will be returned if the + * memory usage can still not be determined. + * @return integer memory usage of the application (in bytes). + */ + public function getMemoryUsage() + { + if(function_exists('memory_get_usage')) + return memory_get_usage(); + else + { + $output=array(); + if(strncmp(PHP_OS,'WIN',3)===0) + { + exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST',$output); + return isset($output[5])?preg_replace('/[\D]/','',$output[5])*1024 : 0; + } + else + { + $pid=getmypid(); + exec("ps -eo%mem,rss,pid | grep $pid", $output); + $output=explode(" ",$output[0]); + return isset($output[1]) ? $output[1]*1024 : 0; + } + } + } + + /** + * Returns the profiling results. + * The results may be filtered by token and/or category. + * If no filter is specified, the returned results would be an array with each element + * being array($token,$category,$time). + * If a filter is specified, the results would be an array of timings. + * @param string token filter. Defaults to null, meaning not filtered by token. + * @param string category filter. Defaults to null, meaning not filtered by category. + * @param boolean whether to refresh the internal timing calculations. If false, + * only the first time calling this method will the timings be calculated internally. + * @return array the profiling results. + * @since 1.0.6 + */ + public function getProfilingResults($token=null,$category=null,$refresh=false) + { + if($this->_timings===null || $refresh) + $this->calculateTimings(); + if($token===null && $category===null) + return $this->_timings; + $results=array(); + foreach($this->_timings as $timing) + { + if(($category===null || $timing[1]===$category) && ($token===null || $timing[0]===$token)) + $results[]=$timing[2]; + } + return $results; + } + + private function calculateTimings() + { + $this->_timings=array(); + + $stack=array(); + foreach($this->_logs as $log) + { + if($log[1]!==CLogger::LEVEL_PROFILE) + continue; + list($message,$level,$category,$timestamp)=$log; + if(!strncasecmp($message,'begin:',6)) + { + $log[0]=substr($message,6); + $stack[]=$log; + } + else if(!strncasecmp($message,'end:',4)) + { + $token=substr($message,4); + if(($last=array_pop($stack))!==null && $last[0]===$token) + { + $delta=$log[3]-$last[3]; + $this->_timings[]=array($message,$category,$delta); + } + else + throw new CException(Yii::t('yii','CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.', + array('{token}'=>$token))); + } + } + + $now=microtime(true); + while(($last=array_pop($stack))!==null) + { + $delta=$now-$last[3]; + $this->_timings[]=array($last[0],$last[2],$delta); + } + } +} diff --git a/web/framework-1.0.8/logging/CProfileLogRoute.php b/web/framework-1.0.8/logging/CProfileLogRoute.php new file mode 100644 index 0000000..b07e81d --- /dev/null +++ b/web/framework-1.0.8/logging/CProfileLogRoute.php @@ -0,0 +1,201 @@ +<?php +/** + * CProfileLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CProfileLogRoute displays the profiling results in Web page. + * + * The profiling is done by calling {@link YiiBase::beginProfile()} and {@link YiiBase::endProfile()}, + * which marks the begin and end of a code block. + * + * CProfileLogRoute supports two types of report by setting the {@link setReport report} property: + * <ul> + * <li>summary: list the execution time of every marked code block</li> + * <li>callstack: list the mark code blocks in a hierarchical view reflecting their calling sequence.</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CProfileLogRoute.php 1043 2009-05-21 20:09:27Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CProfileLogRoute extends CWebLogRoute +{ + /** + * @var boolean whether to aggregate results according to profiling tokens. + * If false, the results will be aggregated by categories. + * Defaults to true. Note that this property only affects the summary report + * that is enabled when {@link report} is 'summary'. + * @since 1.0.6 + */ + public $groupByToken=true; + /** + * @var string type of profiling report to display + */ + private $_report='summary'; + + /** + * Initializes the route. + * This method is invoked after the route is created by the route manager. + */ + public function init() + { + $this->levels=CLogger::LEVEL_PROFILE; + } + + /** + * @return string the type of the profiling report to display. Defaults to 'summary'. + */ + public function getReport() + { + return $this->_report; + } + + /** + * @param string the type of the profiling report to display. Valid values include 'summary' and 'callstack'. + */ + public function setReport($value) + { + if($value==='summary' || $value==='callstack') + $this->_report=$value; + else + throw new CException(Yii::t('yii','CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".', + array('{report}'=>$value))); + } + + /** + * Displays the log messages. + * @param array list of log messages + */ + public function processLogs($logs) + { + $app=Yii::app(); + if(!($app instanceof CWebApplication) || $app->getRequest()->getIsAjaxRequest()) + return; + + if($this->getReport()==='summary') + $this->displaySummary($logs); + else + $this->displayCallstack($logs); + } + + /** + * Displays the callstack of the profiling procedures for display. + * @param array list of logs + */ + protected function displayCallstack($logs) + { + $stack=array(); + $level=0; + $results=array(); + $n=0; + foreach($logs as $log) + { + if($log[1]!==CLogger::LEVEL_PROFILE) + continue; + $message=$log[0]; + if(!strncasecmp($message,'begin:',6)) + { + $log[0]=substr($message,6); + $log[4]=$n; + $stack[]=$log; + $n++; + } + else if(!strncasecmp($message,'end:',4)) + { + $token=substr($message,4); + if(($last=array_pop($stack))!==null && $last[0]===$token) + { + $delta=$log[3]-$last[3]; + $results[$last[4]]=array($token,$delta,count($stack)); + } + else + throw new CException(Yii::t('yii','CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.', + array('{token}'=>$token))); + } + } + // remaining entries should be closed here + $now=microtime(true); + while(($last=array_pop($stack))!==null) + $results[$last[4]]=array($last[0],$now-$last[3],count($stack)); + ksort($results); + $this->render('profile-callstack',$results); + } + + /** + * Displays the summary report of the profiling result. + * @param array list of logs + */ + protected function displaySummary($logs) + { + $stack=array(); + foreach($logs as $log) + { + if($log[1]!==CLogger::LEVEL_PROFILE) + continue; + $message=$log[0]; + if(!strncasecmp($message,'begin:',6)) + { + $log[0]=substr($message,6); + $stack[]=$log; + } + else if(!strncasecmp($message,'end:',4)) + { + $token=substr($message,4); + if(($last=array_pop($stack))!==null && $last[0]===$token) + { + $delta=$log[3]-$last[3]; + if(!$this->groupByToken) + $token=$log[2]; + if(isset($results[$token])) + $results[$token]=$this->aggregateResult($results[$token],$delta); + else + $results[$token]=array($token,1,$delta,$delta,$delta); + } + else + throw new CException(Yii::t('yii','CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.', + array('{token}'=>$token))); + } + } + + $now=microtime(true); + while(($last=array_pop($stack))!==null) + { + $delta=$now-$last[3]; + $token=$this->groupByToken ? $last[0] : $last[2]; + if(isset($results[$token])) + $results[$token]=$this->aggregateResult($results[$token],$delta); + else + $results[$token]=array($token,1,$delta,$delta,$delta); + } + + $entries=array_values($results); + $func=create_function('$a,$b','return $a[4]<$b[4]?1:0;'); + usort($entries,$func); + + $this->render('profile-summary',$entries); + } + + /** + * Aggregates the report result. + * @param array log result for this code block + * @param float time spent for this code block + */ + protected function aggregateResult($result,$delta) + { + list($token,$calls,$min,$max,$total)=$result; + if($delta<$min) + $min=$delta; + else if($delta>$max) + $max=$delta; + $calls++; + $total+=$delta; + return array($token,$calls,$min,$max,$total); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/logging/CWebLogRoute.php b/web/framework-1.0.8/logging/CWebLogRoute.php new file mode 100644 index 0000000..270c96a --- /dev/null +++ b/web/framework-1.0.8/logging/CWebLogRoute.php @@ -0,0 +1,57 @@ +<?php +/** + * CWebLogRoute class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebLogRoute shows the log content in Web page. + * + * The log content can appear either at the end of the current Web page + * or in FireBug console window (if {@link showInFireBug} is set true). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CWebLogRoute.php 639 2009-02-08 16:16:26Z qiang.xue $ + * @package system.logging + * @since 1.0 + */ +class CWebLogRoute extends CLogRoute +{ + /** + * @var boolean whether the log should be displayed in FireBug instead of browser window. Defaults to false. + */ + public $showInFireBug=false; + + /** + * Displays the log messages. + * @param array list of log messages + */ + public function processLogs($logs) + { + $this->render('log',$logs); + } + + /** + * Renders the view. + * @param string the view name (file name without extension). The file is assumed to be located under framework/data/views. + * @param array data to be passed to the view + */ + protected function render($view,$data) + { + if($this->showInFireBug) + $view.='-firebug'; + else + { + $app=Yii::app(); + if(!($app instanceof CWebApplication) || $app->getRequest()->getIsAjaxRequest()) + return; + } + $viewFile=YII_PATH.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$view.'.php'; + include(Yii::app()->findLocalizedFile($viewFile,'en')); + } +} + diff --git a/web/framework-1.0.8/messages/bg/yii.php b/web/framework-1.0.8/messages/bg/yii.php new file mode 100644 index 0000000..7dd7114 --- /dev/null +++ b/web/framework-1.0.8/messages/bg/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '', + '< Previous' => '', + '<< First' => '', + 'Active Record requires a "db" CDbConnection application component.' => '', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Go to page: ' => '', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'Last >>' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'Next >' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be published does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => '', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => '', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => '', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => '', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => '', + '{attribute} "{value}" has already been taken.' => '', + '{attribute} cannot be blank.' => '', + '{attribute} is invalid.' => '', + '{attribute} is not a valid URL.' => '', + '{attribute} is not a valid email address.' => '', + '{attribute} is not in the list.' => '', + '{attribute} is of the wrong length (should be {length} characters).' => '', + '{attribute} is too big (maximum is {max}).' => '', + '{attribute} is too long (maximum is {max} characters).' => '', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '', + '{attribute} must be an integer.' => '', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '', + '{className} does not support add() functionality.' => '', + '{className} does not support delete() functionality.' => '', + '{className} does not support flush() functionality.' => '', + '{className} does not support get() functionality.' => '', + '{className} does not support set() functionality.' => '', + '{class} does not have a method named "{name}".' => '', + '{class} does not have attribute "{attribute}".' => '', + '{class} does not have attribute "{name}".' => '', + '{class} does not have relation "{name}".' => '', + '{class} does not support fetching all table names.' => '', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '', +); diff --git a/web/framework-1.0.8/messages/config.php b/web/framework-1.0.8/messages/config.php new file mode 100644 index 0000000..e82c91b --- /dev/null +++ b/web/framework-1.0.8/messages/config.php @@ -0,0 +1,19 @@ +<?php +/** + * This is the configuration for generating message translations + * for the Yii framework. It is used by the 'yiic message' command. + */ +return array( + 'sourcePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', + 'messagePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'messages', + 'languages'=>array('zh_cn','zh_tw','de','es','sv','he','nl','pt','ru','it','fr','ja','pl','hu','ro','id','vi','bg'), + 'fileTypes'=>array('php'), + 'exclude'=>array( + '.svn', + 'yiilite.php', + '/i18n/data', + '/messages', + '/vendors', + '/web/js', + ), +); \ No newline at end of file diff --git a/web/framework-1.0.8/messages/de/yii.php b/web/framework-1.0.8/messages/de/yii.php new file mode 100644 index 0000000..8d6aa92 --- /dev/null +++ b/web/framework-1.0.8/messages/de/yii.php @@ -0,0 +1,221 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: yii.php 1100 2009-06-07 12:58:45Z qiang.xue $ + */ +return array ( + 'Active record class "{class}" does not have a scope named "{scope}".' => 'ActiveRecord-Klasse "{class}" hat keinen benannten Bereich "{scope}".', + 'Base path "{path}" is not a valid directory.' => 'Basispfad "{path}" ist kein gültiges Verzeichnis.', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => 'CEAcceleratorCache erfordert, dass die PHP eAccelerator Erweiterung geladen und aktiviert oder mit der "--with-eaccelerator-shared-memory"-Option kompiliert wurde.', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => 'CZendDataCache efordert, dass die PHP Zend Data Cache Erweiterung geladen wurde.', + 'Column name must be either a string or an array.' => 'Spaltenname muss entweder ein String oder ein Array sein.', + 'Table "{table}" does not exist.' => 'Tabelle "{table}" existiert nicht.', + 'The STAT relation "{name}" cannot have child relations.' => 'Die STAT-Relation "{name}" kann keine Kind-Relationen haben.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Die Relation "{relation}" in der ActiveRecord-Klasse "{class}" ist nicht korrekt definiert. Die im Fremdschlüssel angegebene Join-Tabelle "{joinTable}", wurde nicht in der Datenbank gefunden', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => 'Die Relation "{relation}" in der ActiveRecord-Klasse "{class}" wurde mit dem Fremschlüssel "{key}" angegeben, der nicht auf die Elterntabelle "{table}" verweist.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => 'Die Relation "{relation}" in der ActiveRecord-Klasse "{class}" wurde mit einem ungültigen Fremdschlüssel "{key}" definiert. In der Tabelle "{table}" gibt es keine solche Spalte.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => 'Die Relation "{relation}" in der ActiveRecord-Klasse "{class}" wurde mit einem ungültigen Fremdschlüssel definiert. Die Spalten im Fremdschlüssel müssen mit dem Primärschlüsseln der Tabelle "{table}" übereinstimmen.', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => 'Der Wert für die Spalte "{column}" wird bei der Abfrage der Tabelle "{table}" nicht zurückgeliefert.', + '{attribute} "{value}" is invalid.' => '{attribute} "{value}" ist ungültig.', + '"{path}" is not a valid directory.' => '"{path}" ist kein gültiges Verzeichnis.', + '< Previous' => '< Vorherige', + '<< First' => '<< Erste', + 'Active Record requires a "db" CDbConnection application component.' => 'ActiveRecord erfordert eine "db" CDbConnection Applikations-Komponente.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'ActiveRecord-Klasse "{class}" hat eine ungültige Konfiguration für die Relation "{relation}". Relations-Typ, verknüpftes ActiveRecord und Fremdschlüssel müssen angegeben werden.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'ActiveRecord "{class}" benutzt das ungültige Feld "{column}" in SELECT. Beachten Sie, dass dieses Feld in der Tabelle existieren oder ein Alias-Ausdruck sein muss.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Der Alias "{alias}" ist ungültig. Stellen Sie sicher, dass er auf ein existierendes Verzeichnis oder eine existierende Datei verweist.', + 'Application base path "{path}" is not a valid directory.' => 'Der Basispfad "{path}" der Applikation ist kein gültiges Verzeichnis.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Der Laufzeit-Pfad "{path}" der Applikation ist ungültig. Bitte stellen Sie sicher, dass der Webserver-Prozess Schreibrechte dafür besitzt.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Autorisierungs-Element "{item}" wurde bereits dem Benutzer "{user}" zugewiesen.', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache erfordert, dass die PHP APC Erweiterung geladen wurde.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" ist ungültig. Bitte stellen Sie sicher, dass das Verzeichnis existiert und der Webserver-Prozess Schreibrechte dafür besitzt.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID ist ungültig. Stellen Sie sicher, dass "{id}" auf eine gültige Cache-Komponente verweist.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" ist ungültig. Konnte im aktuellen Controller keine solche Aktion finden.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" ist ungültig. Bitte stellen Sie sicher, dass sie sich auf die ID einer Applikations-Komponente vom Typ CDbConnection bezieht.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" ist ungültig. Bitte stellen Sie sicher, dass sie sich auf die ID einer Applikations-Komponente vom Typ CDbConnection bezieht.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql kann nicht leer sein.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand konnte das SQL-Statement nicht ausführen: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand konnte das SQL-Statement nicht vorbereiten: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection unterstützt das Lesen von Schemas für {driver}-Datenbanken nicht.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection konnte keine DB-Verbindung herstellen: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection ist inaktiv und kann keine DB-Operationen ausführen.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString kann nicht leer sein.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader ist nicht rewind-fähig. Es ist ein forward-only (nur-vorwärts) Leser.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" ist ungültig. Bitte stellen Sie sicher, dass sie sich auf die ID einer Applikations-Komponente vom Typ CDbConnection bezieht.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute benötigt die Datenbank-Tabelle "{table}" um Nachrichten zu speichern.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" zeigt nicht auf eine gültige Applikations-Komponente vom Typ CDbConnection.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID ist ungültig. Bitte stellen Sie sicher, dass sie sich auf die ID einer Applikations-Komponente vom Typ CDbConnection bezieht', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction ist inaktiv und kann keine Commit- oder Rollback-Operation durchführen.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory kann nicht leer sein.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName kann nicht leer sein.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" zeigt nicht auf ein gültiges Verzeichnis. Stellen Sie sicher, dass das Verzeichnis existiert und der Webserver-Prozess Schreibrechte dafür besitzt.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain kann nur Objekte annehmen die das IFilter-Interface implementieren.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl kann nicht leer sein.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name kann nicht leer sein.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName kann nicht leer sein.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection kann nur CHttpCookie-Objekte enthalten.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest kann die URL des Eingangs-Scripts nicht bestimmen.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest kann die Pfadinformation der Anfrage nicht ermitteln.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest kann die angeforderte URI nicht ermitteln.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode kann nur "none", "allow" oder "only" sein.', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" ist ungültig. Es muss eine ganze Zahl zwischen 0 und 100 sein.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" ist kein gültiges Verzeichnis.', + 'CMemCache server configuration must be an array.' => 'CMemCache Serverkonfiguration muss ein Array sein.', + 'CMemCache server configuration must have "host" value.' => 'CMemCache Serverkonfiguration erfordert einen Wert für "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name ist erforderlich.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute fand einen unzugehörigen Code-Block "{token}". Stellen Sie sicher dass Aufrufe von Yii::beginProfile() und Yii::endProfile() richtig verschachtelt sind.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" ist ungültig. Gültige Werte enthalten "summary" und "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager erfordert, dass die PHP mcrypt Erweiterung geladen wurde, um das Datenverschlüsselungs-Feature nutzen zu können.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey kann nicht leer sein.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation muss entweder "MD5" oder "SHA1" sein.', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey kann nicht leer sein.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> kann nur Objekte der {type}-Klasse beinhalten.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat muss entweder "path" oder "get" sein.', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache erfordert, dass die PHP XCache Erweiterung geladen wurde.', + 'Cache table "{tableName}" does not exist.' => 'Die Cache-Tabelle "{tableName}" existiert nicht.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Kann "{child}" nicht als Kind von "{name}" hinzufügen. Es wurde eine Schleife entdeckt.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Kann "{child}" nicht als Kind von "{parent}" hinzufügen. Es wurde eine Schleife entdeckt.', + 'Cannot add "{name}" as a child of itself.' => 'Kann "{name}" nicht als Kind von sich selbst hinzufügen.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Kann ein Element vom Typ "{child}" nicht als Kind zu einem Element vom Typ "{parent}" hinzufügen.', + 'Either "{parent}" or "{child}" does not exist.' => 'Entweder "{parent}" oder "{child}" existiert nicht.', + 'Error: Table "{table}" does not have a primary key.' => 'Fehler: Tabelle "{table}" hat keinen Primärschlüssel.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Fehler: Tabelle "{table}" hat einen zusammengesetzten Primärschlüssel, was vom crud Kommando nicht unterstützt wird.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Ereignis "{class}.{event}" ist der ungültige Handler "{handler}" zugeordnet.', + 'Event "{class}.{event}" is not defined.' => 'Ereignis "{class}.{event} ist nicht definiert.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Hochgeladene Datei "{file}" konnte nicht auf die Festplatte gespeichert werden.', + 'File upload was stopped by extension.' => 'Datei-Upload wurde von einer Erweiterung angehalten.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filter "{filter}" ist ungültig. Controller "{class}" beinhaltet die Filtermethode "filter{filter}".', + 'Get a new code' => 'Neuen Code erzeugen', + 'Go to page: ' => 'Zu Seite: ', + 'Invalid MO file revision: {revision}.' => 'Ungültige MO-Datei-Revision: {revision}', + 'Invalid MO file: {file} (magic: {magic}).' => 'Ungültige MO-Datei: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Ungültiger Enumerable-Wert "{value}". Bitte stellen Sie sicher er ist in ({enum}) enthalten.', + 'Last >>' => 'Letzte >>', + 'List data must be an array or an object implementing Traversable.' => 'List-Daten müssen ein Array sein oder ein Objekt, das das Interface Traversable implementiert.', + 'List index "{index}" is out of bound.' => 'Listenindex "{index}" ist außerhalb der Grenzen.', + 'Login Required' => 'Anmeldung erforderlich', + 'Map data must be an array or an object implementing Traversable.' => 'Map-Daten müssen ein Array sein oder ein Objekt, das das Interface Traversable implementiert.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Temporäres Verzeichnis zum Speichern der hochgeladenene Datei "{file}" nicht vorhanden.', + 'Next >' => 'Nächste >', + 'No columns are being updated for table "{table}".' => 'Für Tabelle "{table}" werden keine Felder aktualisiert.', + 'No counter columns are being updated for table "{table}".' => 'Für die Tabelle "{table}" werden keine Zähler-Felder aktualisiert.', + 'Object configuration must be an array containing a "class" element.' => 'Objekt-Konfiguration muss ein Array sein, das ein "class"-Element beinhaltet.', + 'Please fix the following input errors:' => 'Bitte beheben Sie folgende Eingabefehler:', + 'Property "{class}.{property}" is not defined.' => 'Eigenschaft "{class}.{property} ist nicht definiert.', + 'Property "{class}.{property}" is read only.' => 'Eigenschaft "{class}.{property} kann nur gelesen werden.', + 'Queue data must be an array or an object implementing Traversable.' => 'Queue-Daten müssen ein Array sein oder ein Objekt, das das Interface Traversable implementiert.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relation "{name}" ist in der ActiveRecord-Klasse "{class}" nicht definiert.', + 'Stack data must be an array or an object implementing Traversable.' => 'Stack-Daten müssen ein Array sein oder ein Objekt, das das Interface Traversable implementiert.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabelle "{table}" hat kein Feld namens "{column"}.', + 'The "filter" property must be specified with a valid callback.' => 'Für "filter" muss ein gültiger Callback angegeben werden.', + 'The "pattern" property must be specified with a valid regular expression.' => 'Für "pattern" muss ein gültiger Regulärer Ausdruck angegeben werden.', + 'The CSRF token could not be verified.' => 'Der CSRF-Token konnte nicht verifiziert werden.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Das URL-Pattern "{pattern}" für die Route "{route}" ist kein gültiger Regulärer Ausdruck.', + 'The active record cannot be deleted because it is new.' => 'Das ActiveRecord-Objekt kann nicht gelöscht werden, weil es neu ist.', + 'The active record cannot be inserted to database because it is not new.' => 'Das ActiveRecord-Objekt kann nicht in die Datenbank eingefügt werden, weil es nicht neu ist.', + 'The active record cannot be updated because it is new.' => 'Das ActiveRecord-Objekt kann nicht aktualisiert werden, weil es neu ist.', + 'The asset "{asset}" to be published does not exist.' => 'Das zu veröffentlichende Asset "{asset}" existiert nicht.', + 'The command path "{path}" is not a valid directory.' => 'Der Kommando-Pfad "{path}" ist kein gültiges Verzeichnis.', + 'The controller path "{path}" is not a valid directory.' => 'Der Controller-Pfad "{path}" ist kein gültiges Verzeichnis.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Die Datei "{file}" kann nicht hochgeladen werden. Nur Dateien mit diesen Endungen sind erlaubt: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Die Datei "{file}" ist zu groß. Die Größe kann {limit} Bytes nicht überschreiten.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Die Datei "{file}" ist zu klein. Die Größe kann {limit} Bytes nicht unterschreiten.', + 'The file "{file}" was only partially uploaded.' => 'Die Datei "{file}" wurde nur teilweise hochgeladen.', + 'The first element in a filter configuration must be the filter class.' => 'Das erste Element in einer Filter-Konfiguration muss eine Filter-Klasse sein.', + 'The item "{name}" does not exist.' => 'Das Element "{name}" existiert nicht.', + 'The item "{parent}" already has a child "{child}".' => 'Das Element "{parent}" hat bereits ein Kind "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Der Layout-Pfad "{path}" ist kein gültiges Verzeichnis.', + 'The list is read only.' => 'Die Liste kann nur gelesen werden.', + 'The map is read only.' => 'Die Map kann nur gelesen werden.', + 'The module path "{path}" is not a valid directory.' => 'Der Modulpfad "{path}" ist kein gültiges Verzeichnis.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Das Schema für das 12-Stunden-Format muss "h" oder "hh" lauten.', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Das Schema für das 24-Stunden-Format muss "H" oder "HH" lauten.', + 'The pattern for AM/PM marker must be "a".' => 'Das Schema für die AM/PM-Auszeichnung muss "a" lauten.', + 'The pattern for day in month must be "F".' => 'Das Schema für den Wochentag im Monat muss "F" lauten.', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Das Schema für den Tag des Jahres muss "D", "DD" oder "DDD" lauten.', + 'The pattern for day of the month must be "d" or "dd".' => 'Das Schema für den Tag des Monats muss "d" oder "dd" lauten.', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Das Schema für den Wochentag muss "E", "EE", "EEE", "EEEE" oder "EEEEE" lauten.', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Das Schema für das Zeitalter muss "z" oder "v" lauten.', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Das Schema für Stunden in AM/PM muss "K" oder "KK" lauten.', + 'The pattern for hour in day must be "k" or "kk".' => 'Das Schema für die Stunde des Tages muss "k" oder "kk" lauten.', + 'The pattern for minutes must be "m" or "mm".' => 'Das Schema für Minuten muss "m" oder "mm" lauten.', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Das Schema für Monate muss "M", "MM", "MMM" oder "MMMM" lauten.', + 'The pattern for seconds must be "s" or "ss".' => 'Das Schema für Sekunden muss "s" oder "ss" lauten.', + 'The pattern for time zone must be "z" or "v".' => 'Das Schema für die Zeitzone muss "z" oder "v" lauten.', + 'The pattern for week in month must be "W".' => 'Das Schema für die Woche im Monat muss "W" lauten.', + 'The pattern for week in year must be "w".' => 'Das Schema für Kalenderwochen muss "w" lauten.', + 'The queue is empty.' => 'Die Queue ist leer.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Die Relation "{relation}" in der ActiveRecord-Klasse "{class}" ist nicht richtig angegeben: die im Fremdschlüssel verwendete JOIN-Tabelle "{joinTable}" kann nicht in der Datenbank gefunden werden.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Zur Relation "{relation} in der ActiveRecord-Klasse "{class}" wurde ein unvollständiger Fremdschlüssel angegeben. Der Fremdschlüssel muss aus Feldern bestehen, die sich auf beide zu joinende Tabellen beziehen.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Zur Relation "{relation} in der ActiveRecord-Klasse "{class}" wurde der ungültige Fremdschlüssel "{key}" angegeben. Der Fremdschlüssel verweist auf keine JOIN-Tabelle.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Zur Relation "{relation} in der ActiveRecord-Klasse "{class}" wurde ein ungültiger Fremdschlüssel angegeben. Der Fremdschlüssel muss dem Format "joinTabelle(fs1,fs2,..)" entsprechen.', + 'The requested view "{name}" is not found.' => 'Der angeforderte View "{name}" wurde nicht gefunden.', + 'The stack is empty.' => 'Der Stack ist leer.', + 'The system is unable to find the requested action "{action}".' => 'Das System konnte die angeforderte Action "{action}" nicht finden.', + 'The system view path "{path}" is not a valid directory.' => 'Der System-View-Pfad "{path}" ist kein gültiges Verzeichnis.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Die Tabelle "{table}" für die ActiveRecord-Klasse "{class}" kann nicht in der Datenbank gefunden werden.', + 'The verification code is incorrect.' => 'Der Prüfcode ist falsch.', + 'The view path "{path}" is not a valid directory.' => 'Der View-Pfad "{path}" ist kein gültiges Verzeichnis.', + 'Theme directory "{directory}" does not exist.' => 'Theme-Verzeichnis "{directory}" existiert nicht.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Dieser Inhalt erfordert den <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Kann ein Element nicht hinzufügen, dass den selben Namen hat, wie ein bereits existierendes Element.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Kann den Element-Namen nicht ändern. Der Name "{name}" wird bereits von einem anderen Element verwendet.', + 'Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.' => 'Anzahl der Datensätze mit zusammengesetztem Primärschlüssel konnte nicht bestimmt werden. Geben sie bitte die SELECT-Option explizit im Abfragekriterium an.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Status-Datei "{file}" der Applikation konnte nicht angelegt werden. Stellen Sie sicher, dass das Verzeichnis, dass die Datei enthält, existiert und der Webserver-Prozess Schreibrechte dafür besitzt.', + 'Unable to lock file "{file}" for reading.' => 'Datei "{file}" kann nicht zum Lesen ge-lockt werden.', + 'Unable to lock file "{file}" for writing.' => 'Datei "{file}" kann nicht zum Schreiben ge-lockt werden.', + 'Unable to read file "{file}".' => 'Datei "{file}" kann nicht gelesen werden.', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Konnte die Aktion "{object}.{method}" nicht erneut durchführen. Die Methode existiert nicht.', + 'Unable to resolve the request "{route}".' => 'Konnte den Request "{route}" nicht auflösen.', + 'Unable to write file "{file}".' => 'Datei "{file}" konnte nicht geschrieben werden.', + 'Unknown authorization item "{name}".' => 'Unbekanntes Autorisierungs-Element "{name}"', + 'Unrecognized locale "{locale}".' => 'Unbekanntes Locale "{locale}".', + 'View file "{file}" does not exist.' => 'View-Datei "{file}" existiert nicht.', + 'Yii application can only be created once.' => 'Eine Yii Applikation kann nur einmal erzeugt werden.', + 'You are not authorized to perform this action.' => 'Sie sind nicht berechtigt, diese Aktion auszuführen.', + 'Your request is not valid.' => 'Ihre Anfrage ist ungültig.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" ist bereits vergeben.', + '{attribute} cannot be blank.' => '{attribute} kann nicht leer sein.', + '{attribute} is invalid.' => '{attribute} ist ungültig.', + '{attribute} is not a valid URL.' => '{attribute} ist keine gültige URL.', + '{attribute} is not a valid email address.' => '{attribute} ist keine gültige E-Mail-Adresse.', + '{attribute} is not in the list.' => '{attribute} ist nicht in der Liste.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribut} hat die falsche Länge (Es sollten {length} Zeichen sein).', + '{attribute} is too big (maximum is {max}).' => '{attribute} ist zu groß (Maximum ist {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} ist zu lang (Maximal {max} Zeichen).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} ist zu kurz (Mindestens {min} Zeichen).', + '{attribute} is too small (minimum is {min}).' => '{attribute} ist zu klein (Minimum ist {min}).', + '{attribute} must be a number.' => '{attribute} muss eine Zahl sein.', + '{attribute} must be an integer.' => '{attribute} muss eine ganze Zahl sein.', + '{attribute} must be repeated exactly.' => '{attribute} muss genau wiederholt werden.', + '{attribute} must be {type}.' => '{attribute} muss {type} sein.', + '{className} does not support add() functionality.' => '{className} unterstützt die Funktionalität add() nicht.', + '{className} does not support delete() functionality.' => '{className} unterstützt die Funktionalität delete() nicht.', + '{className} does not support flush() functionality.' => '{className} unterstützt die Funktionalität flush() nicht.', + '{className} does not support get() functionality.' => '{className} unterstützt die Funktionalität get() nicht.', + '{className} does not support set() functionality.' => '{className} unterstützt die Funktionalität set() nicht.', + '{class} does not have a method named "{name}".' => '{class} hat keine Methode namens "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} hat kein Attribut "{attribute}".', + '{class} does not have relation "{name}".' => '{class} hat keine Relation "{name}".', + '{class} does not support fetching all table names.' => '{class} unterstützt das Beziehen aller Tabellennamen nicht.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} hat eine ungültige Validierungs-Regel. Die Regel muss die zu validierenden Attribute und den Validatornamen enthalten.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} muss "model" und "attribut" oder "name" Eigenschaften festlegen.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin muss auf true gesetzt werden, um cookie-basierte Authentifizierung zu verwenden.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() muss implementiert werden.', + '{controller} cannot find the requested view "{view}".' => '{controller} kann den angeforderten View "{view}" nicht finden.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} enthält falsch verschachtelte Widget-Tags im View "{view}". Ein {widget}-Widget hat keinen endwidget()-Aufruf.', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} hat einen überzähligen endwidget({id})-Aufruf in seinem View.', + '{widget} cannot find the view "{view}".' => '{widget} kann den View "{view}" nicht finden.', +); diff --git a/web/framework-1.0.8/messages/el/yii.php b/web/framework-1.0.8/messages/el/yii.php new file mode 100644 index 0000000..7be45ce --- /dev/null +++ b/web/framework-1.0.8/messages/el/yii.php @@ -0,0 +1,222 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => 'Η διαδρομή "{path}" δεν είναι έγκυρος κατάλογος.', + '< Previous' => '< Προηγούμενο', + '<< First' => '<< Επόμενο', + 'Active Record requires a "db" CDbConnection application component.' => 'Το Active Record απαιτεί ένα "db" CDbConnection συστατικό της εφαρμογής.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Η Active record κλάση "{class}" έχει μια μη έγκυρη παραμετροποίηση για τη σχέση "{relation}". Θα πρέπει να ορίζεται ο τύπος της σχέσης, η σχετιζόμενη κλάση Active record και το ξένο κλειδί.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Η Active record κλάση "{class}" προσπαθεί να επιλέξει μια μη έγκυρη στήλη "{column}". Πρέπει να σημειωθεί, ότι η στήλη θα πρέπει να υπάρχει στον πίνακα ή να είναι μια έκφραση με κάποιο ψευδώνυμο.', + 'Active record class "{class}" does not have a scope named "{scope}".' => 'Η "{class}" Active record κλάση δεν έχει ορατότητα με όνομα "{scope}".', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Το ψευδώνυμο "{alias}" δεν είναι έγκυρο. Επιβεβαιώστε ότι δείχνει σε ένα υπάρχων κατάλογο ή αρχείο.', + 'Application base path "{path}" is not a valid directory.' => 'Η βασική διαδρομή της εφαρμογής "{path}" δεν είναι έγκυρος κατάλογος.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Η βασική διαδρομή εκτέλεσης "{path}" δεν είναι έγκυρη. Επιβεβαιώστε ότι ο κατάλογος είναι εγγράψιμος από τη διεργασία του διακομιστή ιστοσελίδων.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Το αντικείμενο πιστοποίησης "{item}" έχει οριστεί στο χρήστη "{user}".', + 'Base path "{path}" is not a valid directory.' => 'Η βασική διαδρομή "{path}" δεν είναι έγκυρος κατάλογος.', + 'CApcCache requires PHP apc extension to be loaded.' => 'Το CApcCache απαιτεί την επέκταση apc της PHP να έχει φορτωθεί.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'Η διαδρομή CAssetManager.basePath "{path}" δεν είναι έγκυρη. Επιβεβαιώστε ότι ο κατάλογος υπάρχει και είναι εγγράψιμος από τη διεργασία του διακομιστή ιστοσελίδων.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'Το CCacheHttpSession.cacheID δεν είναι έγκυρο. Επιβεβαιώστε ότι το "{id}" αναφέρεται σε ένα έγκυρο συστατικό λανθάνουσας μνήμης της εφαρμογής.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'Το CCaptchaValidator.action "{id}" δεν είναι έγκυρο. Δεν ήταν δυνατή η εύρεση τέτοιας ενέργειας στον τρέχων ελεγκτή.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Το CDbAuthManager.connectionID "{id}" δεν είναι έγκυρο. Επιβεβαιώστε ότι αναφέρεται στο αναγνωριστικό (ID) του συστατικού CDbConnection της εφαρμογής.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Το CDbCache.connectionID "{id}" δεν είναι έγκυρο. Επιβεβαιώστε ότι αναφέρεται στο αναγνωριστικό (ID) του συστατικού CDbConnection της εφαρμογής.', + 'CDbCacheDependency.sql cannot be empty.' => 'Το CDbCacheDependency.sql δεν πρέπει να είναι κενό.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'Το CDbCommand απέτυχε να εκτελέσει τη δήλωση SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'Το CDbCommand απέτυχε να προετοιμάσει τη δήλωση SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'Το CDbConnection δεν υποστηρίζει την ανάγνωση του σχήματος από τον οδηγό {driver} της βάσης δεδομένων.', + 'CDbConnection failed to open the DB connection: {error}' => 'Το CDbConnection απέτυχε το άνοιγμα της σύνδεσης με τη βάση δεδομένων: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'Το CDbConnection είναι ανενεργό και δεν μπορεί να εκτελέσει λειτουργίες βάσης δεδομένων.', + 'CDbConnection.connectionString cannot be empty.' => 'Το CDbConnection.connectionString δεν πρέπει να είναι κενό.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'Το CDbDataReader δεν μπορεί να επιστρέψει πίσω. Είναι αναγνώστης μόνο προς τα εμπρός.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Το CDbHttpSession.connectionID "{id}" δεν είναι έγκυρο. Επιβεβαιώστε ότι αναφέρεται στο αναγνωριστικό (ID) του συστατικού CDbConnection της εφαρμογής.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'Το CDbLogRoute απαιτεί τον πίνακα "{table}" στη βάση δεδομένων για την αποθήκευση των μηνυμάτων ημερολογίου.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'Το CDbLogRoute.connectionID "{id}" δεν δείχνει σε έγκυρο συστατικό CDbConnection της εφαρμογής.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'Το CDbMessageSource.connectionID δεν είναι έγκυρο. Επιβεβαιώστε ότι το "{id}" αναφέρεται σε ένα έγκυρο συστατικό βάσης δεδομένων της εφαρμογής.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'Το CDbTransaction είναι ανενεργό και δεν μπορεί να εκτελέσει την ολοκλήρωση ή ακύρωση ενεργειών.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'Το CDirectoryCacheDependency.directory δεν πρέπει να είναι κενό.', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => 'Το CEAcceleratorCache απαιτεί η επέκταση PHP eAccelerator να είναι φορτωμένη, ενεργοποιημένη ή μεταγλωττισμένη με την επιλογή "--with-eaccelerator-shared-memory".', + 'CFileCacheDependency.fileName cannot be empty.' => 'Το CFileCacheDependency.fileName δεν πρέπει να είναι κενό.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'Το CFileLogRoute.logPath "{path}" δεν δείχνει σε έγκυρο κατάλογο. Επιβεβαιώστε ότι ο κατάλογος υπάρχει και είναι εγγράψιμος από τη διεργασία του διακομιστή ιστοσελίδων.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'Το CFilterChain δέχεται μόνο αντικείμενα που υλοποιούν τη διεπαφή IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'Το CFlexWidget.baseUrl δεν πρέπει να είναι κενό.', + 'CFlexWidget.name cannot be empty.' => 'Το CFlexWidget.name δεν πρέπει να είναι κενό.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'Το CGlobalStateCacheDependency.stateName δεν πρέπει να είναι κενό.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'Το CHttpCookieCollection μπορεί να κρατά μόνο αντικείμενα τύπου CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'Το CHttpRequest δεν μπόρεσε να καθορίσει τη διεύθυνση URL του σεναρίου εισαγωγής.', + 'CHttpRequest is unable to determine the path info of the request.' => 'Το CHttpRequest δεν μπόρεσε να καθορίσει τις πληροφορίες διαδρομής της αίτησης.', + 'CHttpRequest is unable to determine the request URI.' => 'Το CHttpRequest δεν μπόρεσε να καθορίσει το URI αίτησης.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'Το CHttpSession.cookieMode μπορεί να είναι μόνο "none", "allow" ή "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'Η τιμή CHttpSession.gcProbability "{value}" είναι μη έγκυρη. Πρέπει να είναι ακέραιος αριθμός μεταξύ 0 και 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'Το CHttpSession.savePath "{path}" δεν είναι έγκυρος κατάλογος.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'Το CMemCache απαιτεί να έχει φορτωθεί η επέκταση memcache της PHP.', + 'CMemCache server configuration must be an array.' => 'Η παραμετροποίηση διακομιστή του CMemCache πρέπει να είναι πίνακας.', + 'CMemCache server configuration must have "host" value.' => 'Η παραμετροποίηση διακομιστή του CMemCache πρέπει να περιέχει την τιμή "host".', + 'CMultiFileUpload.name is required.' => 'Το CMultiFileUpload.name είναι απαραίτητο.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'Το CProfileLogRoute βρήκε ένα τμήμα κώδικα "{token}" που δεν ταιριάζει. Επιβεβαιώστε ότι οι κλήσεις στο Yii::beginProfile() και Yii::endProfile() είναι σωστά φωλιασμένες.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'Το CProfileLogRoute.report "{report}" είναι μη έγκυρο. Οι έγκυρες τιμές περιλαμβάνουν τα "summary" και "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'Το CSecurityManager απαιτεί να έχει φορτωθεί η επέκταση mcrypt της PHP προκειμένου να χρησιμοποιηθεί το χαρακτηριστικό κρυπτογράφησης των δεδομένων.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'Το CSecurityManager.encryptionKey δεν πρέπει να είναι κενό.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'Το CSecurityManager.validation πρέπει να είναι είτε "MD5" είτε "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'Το CSecurityManager.validationKey δεν πρέπει να είναι κενό.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'Το CTypedList<{type}> μπορεί να περιέχει μόνο αντικείμενα τύπου {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'Το CUrlManager.UrlFormat μπορεί να είναι είτε "path" είτε "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'Το CXCache απαιτεί να έχει φορτωθεί η επέκταση XCache της PHP.', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => 'Το CZendDataCache απαιτεί να έχει φορτωθεί η επέκταση Zend Data Cache της PHP.', + 'Cache table "{tableName}" does not exist.' => 'Ο πίνακας λανθάνουσας μνήμης "{tableName}" δεν υπάρχει.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Δεν είναι δυνατή η πρόσθεση του "{child}" ως παιδί του "{name}". Ανιχνεύθηκε βρόχος.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Δεν είναι δυνατή η πρόσθεση του "{child}" ως παιδί του "{name}". Ανιχνεύθηκε βρόχος.', + 'Cannot add "{name}" as a child of itself.' => 'Δεν είναι δυνατή η πρόσθεση του "{name}" ως παιδί του εαυτού του.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Δεν είναι δυνατή η πρόσθεση αντικειμένου του τύπου "{child}" σε αντικείμενο τύπου "{parent}".', + 'Column name must be either a string or an array.' => 'Το όνομα στήλης πρέπει να είναι είτε αλφαριθμητικό είτε πίνακας.', + 'Either "{parent}" or "{child}" does not exist.' => 'Είτε το "{parent}" είτε το "{child}" δεν υπάρχει.', + 'Error: Table "{table}" does not have a primary key.' => 'Σφάλμα: Ο πίνακας "{table}" δεν έχει κύριο κλειδί.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Σφάλμα: Ο πίνακας "{table}" έχει σύνθετο κύριο κλειδί που δεν υποστηρίζεται από την εντολή crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Το συμβάν "{class}.{event}" έχει συνδεθεί με χρήση ενός μη έγκυρου χειριστή "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Το συμβάν "{class}.{event}" δεν έχει οριστεί.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Δεν ήταν δυνατή η εγγραφή του απεσταλμένου αρχείου "{file}" στο δίσκο.', + 'File upload was stopped by extension.' => 'Η αποστολή του αρχείου διακόπηκε από επέκταση.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Το φίλτρο "{filter}" είναι μη έγκυρο. Ο ελεγκτής "{class}" δεν περιέχει τη μέθοδο "filter{filter}".', + 'Get a new code' => 'Δημιουργία νέου κωδικού', + 'Go to page: ' => 'Μετάβαση στη σελίδα: ', + 'Invalid MO file revision: {revision}.' => 'Μη έγκυρος αριθμός αρχείου MO αναθεώρησης: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Μη έγκυρο αρχείο MO: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Μη έγκυρη τιμή απαρίθμησης "{value}". Βεβαιωθείτε ότι είναι μεταξύ ({enum}).', + 'Last >>' => 'Τελευταίο >>', + 'List data must be an array or an object implementing Traversable.' => 'Τα δεδομένα λίστας πρέπει να είναι πίνακας ή αντικείμενο που υλοποιεί το Traversable.', + 'List index "{index}" is out of bound.' => 'Ο δείκτης λίστας "{index}" είναι εκτός ορίων.', + 'Login Required' => 'Απαιτείται σύνδεση', + 'Map data must be an array or an object implementing Traversable.' => 'Τα δεδομένα τύπου Map πρέπει να είναι πίνακας ή αντικείμενο που υλοποιεί το Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Δεν υπάρχει ο προσωρινός φάκελος για την αποθήκευση του ανεβασμένου αρχείου "{file}".', + 'Next >' => 'Επόμενο >', + 'No columns are being updated for table "{table}".' => 'Δεν ενημερώνονται στήλες για τον πίνακα "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Δεν ενημερώνονται στήλες για τον πίνακα "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Η παραμετροποίηση αντικειμένου πρέπει να είναι πίνακας που να περιέχει ένα στοιχείο "class".', + 'Please fix the following input errors:' => 'Παρακαλώ διορθώστε τα παρακάτω σφάλματα:', + 'Property "{class}.{property}" is not defined.' => 'Η ιδιότητα "{class}.{property}" δεν έχει ορισθεί.', + 'Property "{class}.{property}" is read only.' => 'Η ιδιότητα "{class}.{property}" είναι μόνο για ανάγνωση.', + 'Queue data must be an array or an object implementing Traversable.' => 'Τα δεδομένα ουράς πρέπει να είναι πίνακας ή αντικείμενο που υλοποιεί το Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Η σχέση "{name}" δεν έχει οριστεί για την active record κλάση "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Τα δεδομένα στοίβας πρέπει να είναι πίνακας ή αντικείμενο που υλοποιεί το Traversable.', + 'Table "{table}" does not exist.' => 'Ο πίνακας "{table}" δεν υπάρχει.', + 'Table "{table}" does not have a column named "{column}".' => 'Ο πίνακας "{table}" δεν έχει στήλη με όνομα "{column}".', + 'The "filter" property must be specified with a valid callback.' => 'Η ιδιότητα "filter" πρέπει να οριστεί με ένα έγκυρο callback.', + 'The "pattern" property must be specified with a valid regular expression.' => 'Η ιδιότητα "pattern" πρέπει να οριστεί με μια έγκυρη κανονική έκφραση.', + 'The CSRF token could not be verified.' => 'Ήταν αδύνατη η επαλήθευση του συμβόλου CSRF.', + 'The STAT relation "{name}" cannot have child relations.' => 'Η σχέση STAT "{name}" δεν μπορεί να έχει σχέσεις-παιδιά.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Το URL πρότυπο "{pattern}" για τη διαδρομή "{route}" πρέπει να είναι έγκυρη κανονική έκφραση.', + 'The active record cannot be deleted because it is new.' => 'Δεν είναι δυνατή η διαγραφή του active record, διότι είναι καινούριο.', + 'The active record cannot be inserted to database because it is not new.' => 'Δεν είναι δυνατή η εισαγωγή του active record στη βάση δεδομένων, διότι δεν είναι καινούριο.', + 'The active record cannot be updated because it is new.' => 'Δεν είναι δυνατή η ενημέρωση του active record, διότι είναι καινούριο.', + 'The asset "{asset}" to be published does not exist.' => 'Το στοιχείο "{asset}" για δημοσίευση δεν υπάρχει.', + 'The command path "{path}" is not a valid directory.' => 'Η διαδρομή εντολής "{path}" δεν είναι έγκυρος κατάλογος.', + 'The controller path "{path}" is not a valid directory.' => 'Η διαδρομή ελεγκτή "{path}" δεν είναι έγκυρος κατάλογος.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Δεν ήταν δυνατό το ανέβασμα του αρχείου "{file}". Επιτρέπονται μόνο αρχεία με τις ακόλουθες επεκτάσεις: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Το αρχείο "{file}" είναι πολύ μεγάλο. Το μέγεθός του δεν μπορεί να υπερβαίνει τα {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Το αρχείο "{file}" είναι πολύ μικρό. Το μέγεθός του δεν μπορεί να είναι μικρότερο των {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'Το αρχείο "{file}" ανέβηκε μόνο μερικώς.', + 'The first element in a filter configuration must be the filter class.' => 'Το πρώτο στοιχείο στην παραμετροποίηση ενός φίλτρου πρέπει να είναι η κλάση φίλτρου.', + 'The item "{name}" does not exist.' => 'Το αντικείμενο "{name}" δεν υπάρχει.', + 'The item "{parent}" already has a child "{child}".' => 'Το αντικείμενο "{name}" έχει ήδη ένα παιδί "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Η διαδρομή διάταξης "{path}" δεν είναι έγκυρος κατάλογος.', + 'The list is read only.' => 'Η λίστα είναι μόνο για ανάγνωση.', + 'The map is read only.' => 'Ο χάρτης είναι μόνο για ανάγνωση.', + 'The module path "{path}" is not a valid directory.' => 'Η διαδρομή μονάδων "{path}" δεν είναι έγκυρος κατάλογος.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Το πρότυπο για την 12-ωρη μορφή ώρας πρέπει να είναι "h" ή "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Το πρότυπο για την 24-ωρη μορφή ώρας πρέπει να είναι "H" ή "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Το πρότυπο για το σύμβολο πμ/μμ πρέπει να είναι "a".', + 'The pattern for day in month must be "F".' => 'Το πρότυπο για την ημέρα μέσα σε ένα μήνα πρέπει να είναι "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Το πρότυπο για την ημέρα μέσα σε ένα έτος πρέπει να είναι "D", "DD" ή "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Το πρότυπο για την ημέρα του μήνα πρέπει να είναι "d" ή "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Το πρότυπο για την ημέρα της εβδομάδας πρέπει να είναι "E", "EE", "EEE", "EEEE" ή "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Το πρότυπο για την ιστορική περίοδο πρέπει να είναι "G", "GG", "GGG", "GGGG" ή "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Το πρότυπο για την ώρα σε πμ/μμ πρέπει να είναι "K" ή "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Το πρότυπο για την ώρα μέσα στην ημέρα πρέπει να είναι "k" ή "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Το πρότυπο για τα λεπτά πρέπει να είναι "m" ή "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Το πρότυπο για το μήνα πρέπει να είναι "M", "MM", "MMM" ή "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Το πρότυπο για τα λεπτά πρέπει να είναι "s" ή "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Το πρότυπο για την ώρα ζώνης πρέπει να είναι "z" ή "v".', + 'The pattern for week in month must be "W".' => 'Το πρότυπο για την εβδομάδα μέσα σε ένα μήνα πρέπει να είναι "W".', + 'The pattern for week in year must be "w".' => 'Το πρότυπο για την εβδομάδα μέσα σε ένα έτος πρέπει να είναι "w".', + 'The queue is empty.' => 'Η ουρά είναι άδεια.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Η σχέση "{relation}" στην active record κλάση "{class}" δεν έχει οριστεί σωστά. Ο πίνακας σύζευξης "{joinTable}" που δίνεται στο ξένο κλειδί δεν μπορεί να βρεθεί στη βάση δεδομένων.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Η σχέση "{relation}" στην active record κλάση "{class}" δεν έχει οριστεί σωστά: Ο πίνακας σύζευξης "{joinTable}" που δίνεται στο ξένο κλειδί δεν μπορεί να βρεθεί στη βάση δεδομένων.', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με το ξένο κλειδί "{key}" που δεν δείχνει στον γονικό πίνακα "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με ελλειπές ξένο κλειδί. Το ξένο κλειδί θα πρέπει να περιέχει τις στήλες που αναφέρονται στους συζευγμένους πίνακες.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με μη έγκυρο ξένο κλειδί "{key}". Το ξένο κλειδί δεν δείχνει σε κανέναν από τους συζευγμένους πίνακες.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με μη έγκυρο ξένο κλειδί "{key}". Δεν υπάρχει τέτοια στήλη στον πίνακα "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με μη έγκυρο ξένο κλειδί. Οι στήλες στο κλειδί πρέπει να δείχνουν στο κύριο κλειδί του πίνακα "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Η σχέση "{relation}" στην active record κλάση "{class}" έχει οριστεί με μη έγκυρο ξένο κλειδί. Η μορφή του ξένου κλειδιού πρέπει να είναι "συζευγμένοςΠίνακας(ξκ1,ξκ2,...)".', + 'The requested view "{name}" is not found.' => 'Δεν ήταν δυνατή η εύρεση της όψης "{name}".', + 'The stack is empty.' => 'Η στοίβα είναι άδεια.', + 'The system is unable to find the requested action "{action}".' => 'Το σύστημα ήταν αδύνατο να εντοπίσει την ζητούμενη ενέργεια "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Η διαδρομή όψεων του συστήματος "{path}" δεν είναι έγκυρος κατάλογος.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Δεν ήταν δυνατή η εύρεση στη βάση δεδομένων του πίνακα "{table}" για την active record κλάση "{class}".', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => 'Δεν παρέχεται η τιμή της στήλης "{column}" κατά την εκτέλεση ερωτήματος στον πίνακα "{table}".', + 'The verification code is incorrect.' => 'Ο κώδικας επαλήθευσης δεν είναι σωστός.', + 'The view path "{path}" is not a valid directory.' => 'Η διαδρομή όψεων "{path}" δεν είναι έγκυρος κατάλογος.', + 'Theme directory "{directory}" does not exist.' => 'Ο κατάλογος θεματικών παραλλαγών "{directory}" δεν υπάρχει.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Το περιεχόμενο απαιτεί την ύπαρξη του <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Δεν ήταν δυνατή η προσθήκη αντικειμένου του οποίου το όνομα είναι το ίδιο με ένα υπάρχον αντικείμενο.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Δεν ήταν δυνατή η αλλαγή του ονόματος αντικειμένου. Το όνομα "{name}" χρησιμοποιείται ήδη από ένα άλλο αντικείμενο.', + 'Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.' => 'Δεν ήταν δυνατή η καταμέτρηση των εγγραφών με σύνθετο κύριο κλειδί. Δηλώστε ρητά την παράμετρο SELECT στα κριτήρια του ερωτήματος.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Δεν ήταν δυνατή η δημιουργία του αρχείου κατάστασης της εφαρμογής "{file}". Βεβαιωθείτε ότι ο κατάλογος που περιέχει το αρχείο υπάρχει και είναι εγγράψιμος από τη διεργασία του διακομιστή ιστοσελίδων.', + 'Unable to lock file "{file}" for reading.' => 'Δεν ήταν δυνατό το κλείδωμα του αρχείου "{file}" για ανάγνωση.', + 'Unable to lock file "{file}" for writing.' => 'Δεν ήταν δυνατό το κλείδωμα του αρχείου "{file}" για εγγραφή.', + 'Unable to read file "{file}".' => 'Δεν ήταν δυνατή η ανάγνωση του αρχείου "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Δεν ήταν δυνατή η επανεκτέλεση της ενέργειας "{object}.{method}". Η μέθοδος δεν υπάρχει.', + 'Unable to resolve the request "{route}".' => 'Δεν ήταν δυνατή η επίλυση της αίτησης "{route}".', + 'Unable to write file "{file}".' => 'Δεν ήταν δυνατή η εγγραφή του αρχείου {file}".', + 'Unknown authorization item "{name}".' => 'Άγνωστο αντικείμενο πιστοποίησης "{name}".', + 'Unrecognized locale "{locale}".' => 'Η τοπική ρύθμιση "{locale}" δεν αναγνωρίστηκε.', + 'View file "{file}" does not exist.' => 'Το αρχείο όψης "{view}" δεν υπάρχει.', + 'Yii application can only be created once.' => 'Η εφαρμογή Yii μπορεί να δημιουργηθεί μία μόνο φορά.', + 'You are not authorized to perform this action.' => 'Δεν είστε πιστοποιημένος για την εκτέλεση αυτής της ενέργειας.', + 'Your request is not valid.' => 'Η αίτησή σας δεν είναι έγκυρη.', + '{attribute} "{value}" has already been taken.' => 'Η τιμή "{value}" για το πεδίο {attribute} έχει ήδη δοθεί.', + '{attribute} "{value}" is invalid.' => 'Η τιμή "{value}" για το πεδίο {attribute} δεν είναι έγκυρη.', + '{attribute} cannot be blank.' => 'Το πεδίο {attribute} δεν πρέπει να είναι κενό.', + '{attribute} is invalid.' => 'Το {attribute} δεν είναι έγκυρο.', + '{attribute} is not a valid URL.' => 'Το {attribute} δεν είναι έγκυρη διεύθυνση URL.', + '{attribute} is not a valid email address.' => 'Το {attribute} δεν είναι έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.', + '{attribute} is not in the list.' => 'Το {attribute} δεν υπάρχει στη λίστα.', + '{attribute} is of the wrong length (should be {length} characters).' => 'Το {attribute} έχει λάθος μέγεθος (πρέπει να ειναι {length} χαρακτήρων).', + '{attribute} is too big (maximum is {max}).' => 'Το {attribute} είναι πολύ μεγάλο (το μέγιστο είναι {max}).', + '{attribute} is too long (maximum is {max} characters).' => 'Το {attribute} είναι πολύ μεγάλο (το μέγιστο είναι {max} χαρακτήρες).', + '{attribute} is too short (minimum is {min} characters).' => 'Το {attribute} είναι πολύ μικρό (το ελάχιστο είναι {max}).', + '{attribute} is too small (minimum is {min}).' => 'Το {attribute} είναι πολύ μικρό (το ελάχιστο είναι {max} χαρακτήρες).', + '{attribute} must be a number.' => 'Το {attribute} πρέπει να είναι αριθμός.', + '{attribute} must be an integer.' => 'Το {attribute} πρέπει να είναι ακέραιος αριθμός.', + '{attribute} must be repeated exactly.' => 'Το {attribute} πρέπει να επαναλαμβάνεται επακριβώς.', + '{attribute} must be {type}.' => 'Το {attribute} πρέπει να είναι τύπου {type}.', + '{className} does not support add() functionality.' => 'Η κλάση {className} δεν υποστηρίζει τη λειτουργικότητα add().', + '{className} does not support delete() functionality.' => 'Η κλάση {className} δεν υποστηρίζει τη λειτουργικότητα delete().', + '{className} does not support flush() functionality.' => 'Η κλάση {className} δεν υποστηρίζει τη λειτουργικότητα flush().', + '{className} does not support get() functionality.' => 'Η κλάση {className} δεν υποστηρίζει τη λειτουργικότητα get().', + '{className} does not support set() functionality.' => 'Η κλάση {className} δεν υποστηρίζει τη λειτουργικότητα set().', + '{class} does not have a method named "{name}".' => 'Η κλάση {class} δεν έχει μέθοδο με όνομα "{name}".', + '{class} does not have attribute "{attribute}".' => 'Η κλάση {class} δεν έχει πεδίο με όνομα "{attribute}".', + '{class} does not have relation "{name}".' => 'Η κλάση {class} δεν έχει σχέση με όνομα "{name}".', + '{class} does not support fetching all table names.' => 'Η κλάση {class} δεν υποστηρίζει τη λήψη όλων των ονομάτων πινάκων.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => 'Η κλάση {class} έχει ένα μη έγκυρο κανόνα επαλήθευσης. Ο κανόνας πρέπει να ορίζει τα πεδία που θα επαληθεύονται, καθώς και το όνομα του επαληθευτή.', + '{class} must specify "model" and "attribute" or "name" property values.' => 'Η κλάση {class} πρέπει να ορίζει τα "model" και "attribute" ή "name" των τιμών των ιδιοτήτων.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => 'Το {class}.allowAutoLogin πρέπει να οριστεί σε αληθές για να χρησιμοποιηθεί η πιστοποίηση βάσει cookies.', + '{class}::authenticate() must be implemented.' => 'Η μέθοδος {class}::authenticate() πρέπει να οριστεί.', + '{controller} cannot find the requested view "{view}".' => 'Ο ελεγκτής {controller} δεν μπορεί να βρει την καλούμενη όψη "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => 'Ο ελεγκτής {controller} περιέχει λάθος εμφωλευμένες σημάνσεις widget στην όψη "{view}". Το widget {widget} δεν περιέχει την κλήση endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => 'Ο ελεγκτής {controller} έχει μια επιπλέον κλήση endWidget({id}) στην όψη του.', + '{widget} cannot find the view "{view}".' => 'Το widget {widget} δεν μπορεί να βρει την όψη "{view}".', +); diff --git a/web/framework-1.0.8/messages/es/yii.php b/web/framework-1.0.8/messages/es/yii.php new file mode 100644 index 0000000..cfe68f7 --- /dev/null +++ b/web/framework-1.0.8/messages/es/yii.php @@ -0,0 +1,231 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: yii.php 1311 2009-08-09 03:44:38Z qiang.xue $ +*/ +return array ( + 'Active record class "{class}" does not have a scope named "{scope}".' => 'La clase de registro activo "{class}" no tiene un ámbito llamado "{scope}".', + 'Base path "{path}" is not a valid directory.' => 'La ruta base "{path}" no es un directorio válido', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => 'CEAcceleratorCache requiere la extensión eAccelerator de PHP para ser cargada, activada o cargada con la opción "--with-eaccelerator-shared-memory".', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => 'CZendDataCache requiere la extensión PHP Zend Data Cache para poder ser cargada.', + 'Column "{column} does not exist in table "{table}".' => 'La columna "{column}" no existe en la tabla "{table}".', + 'Column name must be either a string or an array.' => 'El nombre de la columna debe ser una cadena o un array.', + 'Table "{table}" does not exist.' => 'La tabla "{table}" no existe.', + 'The STAT relation "{name}" cannot have child relations.' => 'La relación STAT "{name}" no puede tener relaciones hijas.', + 'The module path "{path}" is not a valid directory.' => 'La ruta del módulo "{path}" no es un directorio válido.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'La relación "{relation}" en la clase de registro activo "{class}" está especificada correctamente. La tabla de unión o join "{joinTable}" dada en la llave foránea no se encuentra en la base de datos.', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => 'La relación "{relation}" en la clase de registro activo "{class}" es especificada con una llave foránea "{key}" que no apunta a la tabla padre "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => 'La relación "{relation}" en la clase de registro activo "{class}" es especificada con una llave foránea inválida "{key}". No hay tal columna en la tabla "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => 'La relación "{relation}" en la clase de registro activo "{class}" es especificada con una llave foránea inválida. Las columnas en la llave deben coincidir con las llaves primarias de la tabla "{table}".', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => 'El valor para la columna "{column}" no es suministrado cuando se consulta la tabla "{table}".', + 'Unable to resolve the request "{route}".' => 'No es posible resolver la solicitud "{route}"', + '{attribute} "{value}" is invalid.' => '{attribute} "{value}" es inválido.', + '{class} does not have a method named "{name}".' => '{class} no tiene un método llamado "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} no tiene el atributo "{attribute}', + '{class} does not have relation "{name}".' => '{class} no tiene la relación "{name}".', + '{class} does not support fetching all table names.' => '{class} no soporta traer todos los nombres de las tablas.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute requiere la tabla "{table}" de la base de datos para guardar los mensajes de log.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache requiere que la extensión memcache de PHP se encuentre cargada.', + 'Table "{table}" does not have a primary key defined.' => 'Tabla "{table}" no contiene definida una columna primaria.', + 'The "view" property is required.' => 'La propiedad "view" es requerida.', + 'The asset "{asset}" to be pulished does not exist.' => 'El asset "{asset} a ser publicado no existe.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'La columna "{column}" no es una llave foránea en la tabla "{table}"', + 'The requested controller "{controller}" does not exist.' => 'El controlador "{controller}" solicitado es inexistente.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'El valor de la clave primaria "{key}" no ha sido otorgado cuando se consulto la tabla "{table}".', + 'Unable to find the decorator view "{view}".' => 'No se ha podido encontrar la vista del decorador "{view}".', + 'Unable to find the list item.' => 'No se puede encotrar el item de la lista.', + '{class} does not have attribute "{name}".' => '{class} no contiene el atributo "{name}".', + '"{path}" is not a valid directory.' => '"{path}" no es un directorio válido.', + '< Previous' => '< Anterior', + '<< First' => '<< Primero', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record requiere un componente de aplicación "db" del tipo CDbConnection.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active record "{class}" contiene una configuración de relación inválida "{relation}". La misma debe especificar el tipo de relación, la clase active record relacionada y la clave foranea.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" esta intentando de seleccionar una columna inválida "{column}". Nota: La columna puede existir en la base o ser una expresion con alias.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" es inválido. Verifique que el mismo apunta a un directorio o archivo exisitente.', + 'Application base path "{path}" is not a valid directory.' => 'Ruta base de la aplicación "{path}" no es un directorio válido.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Ruta de runtime de aplicación "{path}" es inválida. Verifique que sea un directorio con permisos de escritura por el proceso que corre el servidor Web.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Elemento de autorización "{item}" ha sido asignado al usuario "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache requiere que la extensión apc de PHP se encuentre cargada.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" es inválido. Verifique que el directorio exista y tenga permisos de escritura por el proceso que corre el servidor Web.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID es inválido. Asegurese que "{id}" refiere a un componente de aplicación de cache válido.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" es inválido. No se há podido encontrar dicha acción en el controlador actual.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" es inválido. Asegurese que se refiere a un ID de un componente de aplicación CDbConnection.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" es inválido. Asegurese que refiera a un ID de un componente de aplicación CDbConnection.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql no puede ser vacío.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand falló al ejecutar la sentencia SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand falló al preparar la sentencia SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection no soporta la lectura del esquema para la base de datos {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection falló al abrir la conexion a la base de datos: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection se encuentra inactiva y no puede realizar operaciones de BD.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString no puede ser vacío', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader no puede volver atras ya que es un lector de un avance únicamente.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" es inválido. Asegurese que refiera a un ID de un componente de aplicación CDbConnection', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" no refiere a un componente de aplicación CDbConnection válido.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID es inválido. Asegurese que "{id}" refiera a un componente de aplicación de base de datos válido.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction se encuentra inactiva y no puede realizar la operación commit ni roll back.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory no puede ser vacío.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName no puede ser vacío.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" no apunta a un directorio válido. Verifique que el directorio exista y tenga permisos de escritura por el proceso que corre el servidor Web.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain solamente puede tomar objetos que implementen la interface IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl no puede ser vacío.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name no puede ser vacío.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName no puede ser vacío.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection solo puede contener objetos CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest no puede determinar la URL de su script de entrada.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest no puede determinar la información de la ruta de la solicitud.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest no puede derminar la URI solicitada', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode solo puede ser "none", "allow" ó "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" es inválido. Debe ser un entero entre 0 y 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" no es un directorio válido.', + 'CMemCache server configuration must be an array.' => 'La configuración del servidor CMemCache debe ser un array.', + 'CMemCache server configuration must have "host" value.' => 'La configuración del servidor CMemCache debe contener un "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name es requerido', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute ha encontrado un bloque de código "{token}" desalineado. Asegurese que las llamadas a Yii::beginProfile() y a Yii::endProfile() esten correctamente anidadas.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" es inválido. Los valores validos son "summary" y "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager requiere que la extensión mcrypt de PHP sea cargada para utilizar la opción de encriptación de datos.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey no puede ser vacío.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation debe ser "MD5" ó "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey no puede ser vacío.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> solo puede contener objetos de la clase {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat debe ser "path" o "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache requiere la extensión XCache de PHP para ser cargado', + 'Cache table "{tableName}" does not exist.' => 'Tabla de cache "{tableName}" inexistente.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'No se puede agregar "{child}" como hijo de "{name}". Un ciclo infinito se há detectado.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'No se puede agregar "{child}" como hijo de "{parent}". Un ciclo infinito se há detectado.', + 'Cannot add "{name}" as a child of itself.' => 'No se puede agregar "{name}" como hijo de sí mismo.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'No se le puede agregar un elemento del tipo "{child}" a otro del tipo "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" o "{child}" es inexistente', + 'Error: Table "{table}" does not have a primary key.' => 'Error: Tabla "{table}" no tiene una clave primaria.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Error: Tabla "{table}" tiene una clave primaria compuesta que no es soportada por el comando crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Evento "{class}"."{event}" tiene asociado un manejador "{handler}" inválido.', + 'Event "{class}.{event}" is not defined.' => 'Evento "{class}"."{event}" no se encuentra definido.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Error al escribir el archivo subido "{file}" al disco.', + 'File upload was stopped by extension.' => 'El upload de archivo fue terminado debido a su extensión.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'El filtro "{filter} es inválido. El controlador "{class}" no contiene el método de filtro "filter{filter}".', + 'Get a new code' => 'Obtenga un nuevo código', + 'Go to page: ' => 'Ir a página: ', + 'Invalid MO file revision: {revision}.' => 'Revisión de archivo MO inválido: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Archivo MO inválido: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Valor de enumerador inválido "{value}". Asegurese que este entre ({enum}).', + 'Last >>' => 'Último >>', + 'List data must be an array or an object implementing Traversable.' => 'Los datos de la lista deben ser un array o un objeto que implemento Traversable.', + 'List index "{index}" is out of bound.' => 'Indice de la lista "{index}" esta fuera del limite.', + 'Login Required' => 'Iniciar sesión requerido.', + 'Map data must be an array or an object implementing Traversable.' => 'Los datos del mapa deben ser un array o un objeto que implemento Traversable', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'La carpeta temoporaria para guardar el archivo subido "{file}" no se encuentra.', + 'Next >' => 'Siguiente >', + 'No columns are being updated for table "{table}".' => 'No se actualizó ninguna columna para la tabla "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Ningun contador de columnas ha sido actualizado para la tabla "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'La configuración del objeto debe ser un array conteniendo un elemento "class".', + 'Please fix the following input errors:' => 'Por favor corrija los siguientes errores de ingreso:', + 'Property "{class}.{property}" is not defined.' => 'Propiedad "{class}"."{property}" no se encuentra definida.', + 'Property "{class}.{property}" is read only.' => 'Propiedad "{class}"."{property}" es de solo lectura..', + 'Queue data must be an array or an object implementing Traversable.' => 'Los datos de la cola deben ser un array o un objeto que implemento Traversable', + 'Relation "{name}" is not defined in active record class "{class}".' => 'La relación "{name}" no se encuentra definida en la clase active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Los datos de la pila deben ser un array o un objeto que implemento Traversable', + 'Table "{table}" does not have a column named "{column}".' => 'Tabla "{table}" no contiene la columna "{column}".', + 'The "filter" property must be specified with a valid callback.' => 'La propiedad "filter" debe ser especificada con un callback válido.', + 'The "pattern" property must be specified with a valid regular expression.' => 'La propiedad "pattern" debe ser especificada con una expresión regular válida.', + 'The CSRF token could not be verified.' => 'Su token CSRF no puede ser verificado.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'El patrón de URL "{pattern}" para la ruta "{route}" no es una expresión regular válida.', + 'The active record cannot be deleted because it is new.' => 'El active record no puede ser eliminado porque es nuevo.', + 'The active record cannot be inserted to database because it is not new.' => 'El active record no puede ser insertado a la base de datos porque no es nuevo.', + 'The active record cannot be updated because it is new.' => 'El active record no puede ser actualizado porque es nuevo.', + 'The asset "{asset}" to be published does not exist.' => 'El asset "{asset} a ser publicado no existe.', + 'The command path "{path}" is not a valid directory.' => 'La ruta de comando "{path}" no es un directorio válido.', + 'The controller path "{path}" is not a valid directory.' => 'La ruta del controlador "{path}" no es un directorio válido.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'El archivo "{file}" no puede ser subido. Solo los archivos con estas extensiones son permitidos: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'El archivo "{file}" es muy grande. Su tamaño no puede exceder {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'El archivo "{file}" es muy chico. Su tamaño no puede ser menor que {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'El archivo "{file}" ha sido subido parcialmente.', + 'The first element in a filter configuration must be the filter class.' => 'El primer elemento en la configuración de un filtro debe ser la clase del filtro.', + 'The item "{name}" does not exist.' => 'El elemento "{name}" es inexistente.', + 'The item "{parent}" already has a child "{child}".' => 'El elemento "{parent}" ya contiene un hijo "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'La ruta de esquema "{path}" no es un directorio válido.', + 'The list is read only.' => 'La lista es de solo lectura', + 'The map is read only.' => 'El mapa es de solo lectura', + 'The pattern for 12 hour format must be "h" or "hh".' => 'El patrón para hora en formato 12 debe ser "h" ó "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'El patrón para hora en formato 24 debe ser "H" ó "HH".', + 'The pattern for AM/PM marker must be "a".' => 'El patrón para el marcador AM/PM debe ser "a".', + 'The pattern for day in month must be "F".' => 'El patrón para día del mes debe ser "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'El patrón para día del año debe ser "D", "DD", "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'El patrón para día debe ser "d" ó "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'El patrón para día de la semana debe ser "E", "EE", "EEE", "EEEE" ó "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'El patrón para era debe ser "G", "GG", "GGG", "GGGG" ó "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'El patrón para hora en AM/PM debe ser "K" ó "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'El patrón para hora del día debe ser "k" ó "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'El patrón para minutos debe ser "m" ó "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'El patrón para mes debe ser "M", "MM", "MMM" ó "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'El patrón para segundos debe ser "s" ó "ss".', + 'The pattern for time zone must be "z" or "v".' => 'El patrón para zona horaria debe ser "z" ó "v".', + 'The pattern for week in month must be "W".' => 'El patron para semana del mes debe ser "W".', + 'The pattern for week in year must be "w".' => 'El patrón para semana del año debe ser "w".', + 'The queue is empty.' => 'La cola está vacía', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'La relación "{relation}" en la clase active record "{class}" no se encuentra especificada correctamente: La tabla de junta (join table) "{join table}" dada no se encontro en la base de datos.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'La relación "{relation}" en la clase active record "{class}" se encuentra especificada con una clave foranea incompleta. La clave foranea debe consistir de las columnas que referencian la junta de tablas.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'La relación "{relation}" en la clase active record "{class}" se encuentra especificada con una clave foranea inválida "{key}". La clave foranea no apunta a la tabla de junta (joining table).', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'La relación "{relation}" en la clase active record "{class}" se encuentra especificada con una clave foranea inválida. El formato de la clave foranea debe ser "joinTable(fk1,fk2,...)".', + 'The requested view "{name}" is not found.' => 'La vista "{name}" solicitad no se ha encontrado.', + 'The stack is empty.' => 'La pila está vacía', + 'The system is unable to find the requested action "{action}".' => 'El sistema no ha podido encontrar la acción "{action}" solicitada.', + 'The system view path "{path}" is not a valid directory.' => 'La ruta de vistas de sistema "{path}" no es un directorio válido.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'La tabla "{table}" definida en la clase active record "{class}" no se ha podido encontrar en la base de datos.', + 'The verification code is incorrect.' => 'El código de verificación es incorrecto.', + 'The view path "{path}" is not a valid directory.' => 'La ruta de la vista "{path}" no es un directorio válido.', + 'Theme directory "{directory}" does not exist.' => 'El directorio de tema "{directory}" no existe.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Este contenido requiere el <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'No se puede agregar un elemento cuyo nombre es el mismo que el de un elemento existente.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'No se puede modificar el nombre del elemento. El nombre "{name}" ya se encuentra utilizado por otro elemento.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'No se ha podido crear el archivo de estado de aplicación "{file}". Asegurese que el directorio que contiene el archivo exista y sea un directorio con permisos de escritura por el proceso que corre el servidor Web.', + 'Unable to lock file "{file}" for reading.' => 'No se há podido bloquear el archivo "{file}" para lectura.', + 'Unable to lock file "{file}" for writing.' => 'No se ha podido bloquear el archivo "{file}" para escritura.', + 'Unable to read file "{file}".' => 'No se ha podido leer el archivo "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Imposible de replicar la acción "{object}.{method}". El metodo es inexistente.', + 'Unable to write file "{file}".' => 'No se ha podido escribir el archivo "{file}".', + 'Unknown authorization item "{name}".' => 'Elemento de autorización "{name}" desconocido.', + 'Unrecognized locale "{locale}".' => 'Localizacion no reconocida "{locale}".', + 'View file "{file}" does not exist.' => 'El archivo de vista "{view}" no existe.', + 'Yii application can only be created once.' => 'Solo se puede crear una aplicación Yii.', + 'You are not authorized to perform this action.' => 'Usted no se encuentra autorizado a realizar esta acción.', + 'Your request is not valid.' => 'Su solicitud es inválida.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" ya ha sido tomado.', + '{attribute} cannot be blank.' => '{attribute} no puede ser nulo.', + '{attribute} is invalid.' => '{attribute} es inválido.', + '{attribute} is not a valid URL.' => '{attribute} no es una URL válida.', + '{attribute} is not a valid email address.' => '{attribute} no es un email válido.', + '{attribute} is not in the list.' => '{attribute} no se encuentra en la lista.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} tiene un largo incorrecto (debe ser de {length} caracteres)', + '{attribute} is too big (maximum is {max}).' => '{attribute} es muy grande (el máximo es {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} es muy largo (el máximo es de {min} caracteres)', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} es muy corto (el mínimo es de {min} caracteres)', + '{attribute} is too small (minimum is {min}).' => '{attribute} es muy chico (el mínimo es {min}).', + '{attribute} must be a number.' => '{attribute} debe ser un número.', + '{attribute} must be an integer.' => '{attribute} debe ser entero.', + '{attribute} must be repeated exactly.' => '{attribute} debe ser repetido exactamente.', + '{attribute} must be {type}.' => '{attribute} debe ser {type}.', + '{className} does not support add() functionality.' => '{className} no soporta la funcionalidad add().', + '{className} does not support delete() functionality.' => '{className} no soporta la funcionalidad delete().', + '{className} does not support flush() functionality.' => '{className} no soporta la funcionalidad flush().', + '{className} does not support get() functionality.' => '{className} no soporta la funcionalidad get().', + '{className} does not support set() functionality.' => '{className} no soporta la funcionalidad set().', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} tiene una regla de validación inválida. La regla se debe especificar attributos para ser validados y el nombre de su validador.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} debe especificar los valores de propiedad "model" y "attribute" o "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin debe ser asignado verdadero para poder utilizar la autenticación basada en cookies.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() debe ser implementad.', + '{controller} cannot find the requested view "{view}".' => '{controller} no ha podido encontrar la vista "{view}" solicitada.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} contiene etiquetas de widget en la vista "{view}" anidados incorrectamente. {widget} widget no contiene la llamada a endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} tiene una llamada extra a endWidget({id}) en su vista.', + '{widget} cannot find the view "{view}".' => '{widget} no ha podido encontrar la vista "{view}".', +); diff --git a/web/framework-1.0.8/messages/fr/yii.php b/web/framework-1.0.8/messages/fr/yii.php new file mode 100644 index 0000000..a729662 --- /dev/null +++ b/web/framework-1.0.8/messages/fr/yii.php @@ -0,0 +1,210 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" n\'est pas un dossier valide.', + 'Active Record requires a "db" CDbConnection application component.' => 'Un composant d\'application "db" de type CDbConnection est nécessaire pour les Active Record.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'La relation "{relation}" de l\'Active Record "{class}" est invalide. La classe et la clef étrangère de l\'active record associé doivent être spécifiées.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'La colonne "{column}" que tente d\'accéder l\'Active Record "{class}" est invalide. Note, la colonne doit exister au sein de la table ou être un l\'alias d\'une expression.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'L\'alias "{alias}" est invalide. Vérifiez qu\'il pointe sur un fichier ou un dossier valide.', + 'Application base path "{path}" is not a valid directory.' => 'Le chemin du dossier de base de l\'application "{path}" est invalide.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Le chemin du dossier d\'exécution "{path}" est invalide. Vérifiez que le proccessus du serveur web peut y accéder en écriture.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'L\'élément d\'autorisation "{item}" a déjà été assigné à l\'utilisateur "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'L\'extension PHP apc doit être chargée pour pouvoir utiliser CApcCache.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'La propriété CAssetManager.basePath "{path}" est invalide. Vérifiez que le dossier existe et que le proccessus du serveur web peut y accéder en écriture.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'La propriété CCacheHttpSession.cacheID "{id}" est invalide. Vérifiez qu\'elle référence l\'ID d\'un composant d\'application de type cache valide.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'La propriété CCaptchaValidator.action "{id}" est invalide. Impossible de trouver cette action dans le contrôleur courant', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'La propriété CDbAuthManager.connectionID "{id}" est invalide. Vérifiez qu\'elle référence l\'ID d\'un composant d\'application de type CDbConnection.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'La propriété CDbCache.connectionID "{id}" est invalide. Vérifiez qu\'elle référence l\'ID d\'un composant d\'application de type CDbConnection.', + 'CDbCacheDependency.sql cannot be empty.' => 'La propriété CDbCacheDependency.sql ne peut être vide.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand n\'a pas pu exécuter la commande SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand n\'a pas pu préparer la commande SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection ne supporte pas la lecture de schéma pour les bases de données {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection n\'a pu ouvrir la connexion à la base de données: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection est inactive et ne peut effectuer aucune opération sur la base de données.', + 'CDbConnection.connectionString cannot be empty.' => 'La propriété CDbConnection.connectionString ne peut être vide.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader ne peut reculer. Il peut seulement avancer.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" est invalide. Vérifiez qu\'il référence l\'ID d\'un composant d\'application de type CDbConnection.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute nécessite une table "{table}" pour enregistrer les messages dans la base de données', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'La propriété CDbLogRoute.connectionID "{id}" ne pointe pas vers un composant d\'application de type CDbConnection valide.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'La propriété CDbMessageSource.connectionI est invalide. Vérifiez que "{id}" référence un composant d\'application de base de données valide.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction est inactif et ne peut donc valider ou annuler des opérations.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'La propriété CDirectoryCacheDependency.directory ne peut être vide.', + 'CFileCacheDependency.fileName cannot be empty.' => 'La propriété CFileCacheDependency.fileName ne peut être vide.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" ne pointe pas vers un dossier valide. Vérifiez que le proccessus du serveur web peut y accéder en écriture.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain peut seulement accepter des objets qui implémentent l\'interface IFilter', + 'CFlexWidget.baseUrl cannot be empty.' => 'La propriété CFlexWidget.baseUrl ne peut être vide.', + 'CFlexWidget.name cannot be empty.' => 'La propriété CFlexWidget.name ne peut être vide.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'La propriété CGlobalStateCacheDependency.stateName ne peut être vide.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection peut seulement contenir des objets de type CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest ne peut déterminer le script de base de l\'URL', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest ne peut déterminer le chemin de la requête', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest ne peut déterminer l\'URI de la requête.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'La propriété CHttpSession.cookieMode peut prendre la valeur "none", "allow" or "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'La propriété CHttpSession.gcProbability "{value}" est invalide. Ce doit être un entier compris entre 0 et 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'La valeur de la propriété CHttpSession.savePath "{path}" n\'est pas un dossier valide.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'L\'extension PHP memcache doit être chargée pour pouvoir utiliser CMemCache.', + 'CMemCache server configuration must be an array.' => 'La configuration serveur de CMemCache doit être un array.', + 'CMemCache server configuration must have "host" value.' => 'La configuration serveur de CMemCache doit avoir une valeur "host"', + 'CMultiFileUpload.name is required.' => 'La propriété CMultiFileUpload.name doit être définie.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute a trouvé un bloc de code "{token}" incorrect. Vérifiez que les appels Yii::beginProfile() et Yii::endProfile() sont correctement imbriqués.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'La propriété CProfileLogRoute.report "{report}" est invalide. Les valeurs autorisées sont "summary" et "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'L\'extension PHP mcrypt doit être chargée pour pouvoir utiliser CSecurityManager et activer les fonctionnalités de cryptage.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'La propriété CSecurityManager.encryptionKey ne peut être vide.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'La propriété CSecurityManager.validation peut prendre la valeur "MD5" ou "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'La propriété CSecurityManager.validationKey ne peut être vide.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> peut uniquement contenir des objets de type {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'La propriété CUrlManager.UrlFormat doit être "path" ou "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'L\'extension PHP XCache doit être chargée pour pouvoir utiliser CXCache.', + 'Cache table "{tableName}" does not exist.' => 'La table de cache "{tableName}" n\'existe pas.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Impossible d\'ajouter "{child}" en tant qu\'enfant de "{name}". Une boucle a été détectée.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Impossible d\'ajouter "{child}" en tant qu\'enfant de "{parent}". Une boucle a été détectée.', + 'Cannot add "{name}" as a child of itself.' => 'Impossible d\'ajouter "{name}" en tant qu\'enfant de lui-même.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Impossible d\'ajouter un élément de type "{child}" à un élément de type "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" ou "{child}" est inexistant.', + 'Error: Table "{table}" does not have a primary key.' => 'Erreur: la table "{table}" n\'a pas de clef primaire.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Erreur: la table "{table}" a une clef primaire composite qui n\'est pas supportée par les commandes CRUD.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'L\'événement "{class}.{event}" est associé à un gestionnaire d\'événement "{handler}" invalide.', + 'Event "{class}.{event}" is not defined.' => 'L\'événement "{class}.{event}" n\'est pas défini.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Impossible d\'écrire le fichier téléchargé "{file}" sur le disque', + 'File upload was stopped by extension.' => 'Le téléchargement a été stoppé apr extension.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Le filtre "{filter}" est invalide. Le contrôleur "{class}" n\'implémente pas la méthode de filtrage "filter{filter}".', + 'Get a new code' => 'Récupérez un nouveau code', + 'Invalid MO file revision: {revision}.' => 'Version du fichier MO invalide: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Fichier MO invalide: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Valeur "{value}" invalide. Vérifiez qu\'elle fait partie de ({enum}).', + 'List data must be an array or an object implementing Traversable.' => 'La liste de données doit être un array ou un objet qui implémente Traversable.', + 'List index "{index}" is out of bound.' => 'L\'index "{index}" est en dehors de la liste.', + 'Login Required' => 'Identifiant requis', + 'Map data must be an array or an object implementing Traversable.' => 'Les données doivent être un array ou un objet qui implémente Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Le dossier temporaire permettant de stocker le fichier téléchargédes "{file}" est inexistant.', + 'No columns are being updated for table "{table}".' => 'Aucune colonne de la table "{table}" ne sera mise à jour.', + 'No counter columns are being updated for table "{table}".' => 'Ancune colonne incrémentale de la table "{table}" ne sera mise à jour.', + 'Object configuration must be an array containing a "class" element.' => 'L\'objet configuration doit être un array contenant un élément "class".', + 'Please fix the following input errors:' => 'Veuillez corriger les erreurs de saisie:', + 'Property "{class}.{property}" is not defined.' => 'La propriété "{class}.{property}" est indéfinie.', + 'Property "{class}.{property}" is read only.' => 'La propriété "{class}.{property}" est en lecture seule.', + 'Queue data must be an array or an object implementing Traversable.' => 'Les données de la queue doivent être un array ou un objet qui implémente Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'La relation "{name}" n\'est pas définie dans la classe de l\'active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Les données de la pile doivent être un array ou un objet qui implémente Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'La table "{table}" n\'a pas de colonne "{column}"', + 'Table "{table}" does not have a primary key defined.' => 'Aucune clef primaire n\'est définie pour la table "{table}".', + 'The "filter" property must be specified with a valid callback.' => 'La propriété "filter" doit contenir une fonction de rappel valide.', + 'The "pattern" property must be specified with a valid regular expression.' => 'La propriété "pattern" doit contenir une expression rationnelle valide.', + 'The "view" property is required.' => 'La propriété "view" est requise.', + 'The CSRF token could not be verified.' => 'Le jeton CSRF n\'a pu être vérifié.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'le motif "{pattern}" pour la route "{route}" n\'est pas un expression rationnelle valide.', + 'The active record cannot be deleted because it is new.' => 'L\'active record ne peut être supprimé car il vient d\'être défini.', + 'The active record cannot be inserted to database because it is not new.' => 'L\'active record ne peut être inséré en base de données car il ne vient pas d\'être défini.', + 'The active record cannot be updated because it is new.' => 'L\'active record ne peut être mis à jour car il vient d\'être défini.', + 'The asset "{asset}" to be pulished does not exist.' => 'L\'élément "{asset}" devant être publié est inexistant.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'La colonne "{column}" n\'est pas une clef étrangère de la table "{table}".', + 'The command path "{path}" is not a valid directory.' => 'Le chemin d\'accès à la ligne de commande "{path}" n\'est pas un dossier valide.', + 'The controller path "{path}" is not a valid directory.' => 'Le chemin d\'accès au contrôleur "{path}" n\'est pas un dossier valide.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => + 'Le fichier "{file}" ne peut être téléchargé. Les extensions de fichier autorisées sont: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Le fichier "{file}" est trop gros. Sa taille ne peut être supérieure à {limit} octets.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Le fichier "{file}" est trop petit. Sa taille ne peut être inférieure à {limit} octets.', + 'The file "{file}" was only partially uploaded.' => 'Le fichier "{file}" a été téléchargé partiellement.', + 'The first element in a filter configuration must be the filter class.' => 'Le premier élément de la configuration d\'un filtre doit être la classe filtre.', + 'The item "{name}" does not exist.' => 'L\'élément "{name}" est inexistant.', + 'The item "{parent}" already has a child "{child}".' => 'L\'élément "{parent}" a déjà un enfant "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Le chemin d\'accès au gabarit "{path}" n\'est pas un dossier valide.', + 'The list is read only.' => 'La liste est en lecture seule.', + 'The map is read only.' => 'Le map est en lecture seule.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Le motif de définition des heures au format 12h doit être "h" ou "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Le motif de définition des heures au format 24h doit être "H" ou "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Le motif de définition du marqueur AM/PM doit être "a".', + 'The pattern for day in month must be "F".' => 'Le motif de définition du jour dans le mois doit être "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Le motif de définition du jour dans l\'année doit être "D", "DD" ou "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Le motif de définition du jour du mois doit être "d" ou "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Le motif de définition du jour de las semaine doit être "E", "EE", "EEE", "EEEE" ou "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Le motif de définition de l\'ère doit être "G", "GG", "GGG", "GGGG" ou "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Le motif de définition des heures au format AM/PM doit être "K" ou "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Le motif de définition des heures dans la journée doit être "k" ou "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Le motif de définition des minutes doit être "m" ou "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Le motif de définition du mois doit être "M", "MM", "MMM" ou "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Le motif de définition des secondes doit être "s" ou "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Le motif de définition du fuseau horaire doit être "z" ou "v".', + 'The pattern for week in month must be "W".' => 'Le motif de définition du numéro de semaine dans le mois doit être "W".', + 'The pattern for week in year must be "w".' => 'Le motif de définition du numéro de semaine dans l\'année doit être "w".', + 'The queue is empty.' => 'La queue est vide.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'La relation "{relation}" définie dans l\'active record "{class}" est incorrecte: la table de jointure "{joinTable}" spécifiée par la clef étrangère est introuvable dans la base de données.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'La relation "{relation}" définie dans l\'active record "{class}" a une clef étrangère incomplète. La clef étrangère doit être constituée des colonnes qui référence les deux tables.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'La relation "{relation}" définie dans l\'active record "{class}" a une clef étrangère "{key}" invalide. La clef étrangère ne pointe pas sur une table de jointure.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'La relation "{relation}" définie dans l\'active record "{class}" a une clef étrangère invalide. Le clef étrangère doit être sous le format "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Le contrôleur demandé "{controller}" est inexistant.', + 'The requested view "{name}" is not found.' => 'La vue demandée "{name}" est inexistante.', + 'The stack is empty.' => 'La pile est vide.', + 'The system is unable to find the requested action "{action}".' => 'Le système ne peut trouver l\'action "{action}" demandée.', + 'The system view path "{path}" is not a valid directory.' => 'La chemin "{path}" d\'accès à la vue système n\'est pas un dossier valide.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'La table "{table}" associée à l\'active record "{class}" est introuvable dans la base de données.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'La valeur de la clef primaire "{key}" n\'est pas fournie lors d\'une requête sur la table "{table}".', + 'The verification code is incorrect.' => 'Le code de vérification est incorrect.', + 'The view path "{path}" is not a valid directory.' => 'Le chemin "{path}" d\'accès à la vue n\'est pas un dossier valide.', + 'Theme directory "{directory}" does not exist.' => 'Le dossier "{directory}" des thèmes est inexistant.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Ce contenu nécessite le <a href="http://www.adobe.com/go/getflash/">Lecteur Flash d\'Adobe</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Impossible d\'ajouter un élément dont le nom est identique à celui d\'un autre éléménet.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Impossible de changer le nom de l\'élément. Le nom "{name}" est déjà affecté à un autre élémnent.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => + 'Impossible de créer le fichier "{file}" de gestion d\'état de l\'application. Vérifiez que le fichier existe et que le proccessus du serveur web peut y accéder en écriture.', + 'Unable to find the decorator view "{view}".' => 'Impossible de trouver la vue décorateur "{view}".', + 'Unable to find the list item.' => 'Impossible de trouver l\'élément de la liste.', + 'Unable to lock file "{file}" for reading.' => 'Impossible de verrouiller le fichier "{file}" en lecture.', + 'Unable to lock file "{file}" for writing.' => 'Impossible de verrouiller le fichier "{file}" en écriture.', + 'Unable to read file "{file}".' => 'Impossible de lire le fichier "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Impossible de rejouer l\'action "{object}.{method}". La méthode n\'existe pas.', + 'Unable to write file "{file}".' => 'Impossible d\'écrire dans le fichier "{file}".', + 'Unknown authorization item "{name}".' => 'l\'élément "{name}" d\'autorisation est inconnu.', + 'Unrecognized locale "{locale}".' => 'La locale "{locale}" n\'a pas été reconnu.', + 'View file "{file}" does not exist.' => 'Le fichier de la vue "{file}" est inexistant.', + 'Yii application can only be created once.' => 'Une seule application Yii peut être créée.', + 'You are not authorized to perform this action.' => 'Vous n\'êtes pas autorisé à effectuer cette action.', + 'Your request is not valid.' => 'Votre requête est invalide.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" a déjà été pris.', + '{attribute} cannot be blank.' => '{attribute} ne peut être vide.', + '{attribute} is invalid.' => '{attribute} est invalide.', + '{attribute} is not a valid URL.' => '{attribute} n\'est pas une URL valide.', + '{attribute} is not a valid email address.' => '{attribute} n\'est pas une adresse email valide.', + '{attribute} is not in the list.' => '{attribute} n\'est pas dans la liste.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} n\'est pas de la bonne longueur ({length} caaractères autorisés).', + '{attribute} is too big (maximum is {max}).' => '{attribute} est trop grand (maximum : {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} est trop long (maximum : {max} caractères).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} est trop court (minimum : {min} caractères).', + '{attribute} is too small (minimum is {min}).' => '{attribute} est trop petit (minimum : {min}).', + '{attribute} must be a number.' => '{attribute} doit être un nombre.', + '{attribute} must be an integer.' => '{attribute} doit être un entier.', + '{attribute} must be repeated exactly.' => '{attribute} doit être répété à l\'identique.', + '{attribute} must be {type}.' => '{attribute} doit être {type}.', + '{className} does not support add() functionality.' => '{className} ne supporte pas la fonctionalité add().', + '{className} does not support delete() functionality.' => '{className} ne supporte pas la fonctionalité delete().', + '{className} does not support flush() functionality.' => '{className} ne supporte pas la fonctionalité flush().', + '{className} does not support get() functionality.' => '{className} ne supporte pas la fonctionalité get().', + '{className} does not support set() functionality.' => '{className} ne supporte pas la fonctionalité set().', + '{class} does not have attribute "{attribute}".' => '{class} n\'a pas d\'attribut "{attribute}".', + '{class} does not have attribute "{name}".' => '{class} n\'a pas d\'attribut "{name}".', + '{class} does not have relation "{name}".' => '{class} n\'a pas de relation "{name}".', + '{class} does not support fetching all table names.' => '{class} ne peut récupérer tous les noms des tables.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} a une règle de validation invalide. La règle doit spécifier les attributs à valider et le nom du valideur.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} doit définir le valeurs des propriétés "model" et "attribute" ou "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin doit être à true pour pouvoir utiliser l\'autentification par cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() doit être implémenté.', + '{controller} cannot find the requested view "{view}".' => '{controller} ne peut trouver la vue "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} contient une mauvaise imbrication des tags des widgets dans la vue "{view}". Le widget {widget} n\'a pas d\'appel endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} a un appel à endWidget({id}) en trop dans sa vue.', + '{widget} cannot find the view "{view}".' => '{widget} ne peut trouver la vue "{view}".', +); diff --git a/web/framework-1.0.8/messages/he/yii.php b/web/framework-1.0.8/messages/he/yii.php new file mode 100644 index 0000000..ac2a553 --- /dev/null +++ b/web/framework-1.0.8/messages/he/yii.php @@ -0,0 +1,206 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '', + 'Active Record requires a "db" CDbConnection application component.' => '', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be pulished does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => '', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => '', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => '', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to find the list item.' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => '', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => '', + '{attribute} "{value}" has already been taken.' => '', + '{attribute} cannot be blank.' => '', + '{attribute} is invalid.' => '', + '{attribute} is not a valid URL.' => '', + '{attribute} is not a valid email address.' => '', + '{attribute} is not in the list.' => '', + '{attribute} is of the wrong length (should be {length} characters).' => '', + '{attribute} is too big (maximum is {max}).' => '', + '{attribute} is too long (maximum is {max} characters).' => '', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '', + '{attribute} must be an integer.' => '', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '', + '{className} does not support add() functionality.' => '', + '{className} does not support delete() functionality.' => '', + '{className} does not support flush() functionality.' => '', + '{className} does not support get() functionality.' => '', + '{className} does not support set() functionality.' => '', + '{class} does not have attribute "{name}".' => '', + '{class} does not have relation "{name}".' => '', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '', +); diff --git a/web/framework-1.0.8/messages/hu/yii.php b/web/framework-1.0.8/messages/hu/yii.php new file mode 100644 index 0000000..7dd7114 --- /dev/null +++ b/web/framework-1.0.8/messages/hu/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '', + '< Previous' => '', + '<< First' => '', + 'Active Record requires a "db" CDbConnection application component.' => '', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Go to page: ' => '', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'Last >>' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'Next >' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be published does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => '', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => '', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => '', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => '', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => '', + '{attribute} "{value}" has already been taken.' => '', + '{attribute} cannot be blank.' => '', + '{attribute} is invalid.' => '', + '{attribute} is not a valid URL.' => '', + '{attribute} is not a valid email address.' => '', + '{attribute} is not in the list.' => '', + '{attribute} is of the wrong length (should be {length} characters).' => '', + '{attribute} is too big (maximum is {max}).' => '', + '{attribute} is too long (maximum is {max} characters).' => '', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '', + '{attribute} must be an integer.' => '', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '', + '{className} does not support add() functionality.' => '', + '{className} does not support delete() functionality.' => '', + '{className} does not support flush() functionality.' => '', + '{className} does not support get() functionality.' => '', + '{className} does not support set() functionality.' => '', + '{class} does not have a method named "{name}".' => '', + '{class} does not have attribute "{attribute}".' => '', + '{class} does not have attribute "{name}".' => '', + '{class} does not have relation "{name}".' => '', + '{class} does not support fetching all table names.' => '', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '', +); diff --git a/web/framework-1.0.8/messages/id/yii.php b/web/framework-1.0.8/messages/id/yii.php new file mode 100644 index 0000000..7d3bed7 --- /dev/null +++ b/web/framework-1.0.8/messages/id/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" bukanlah direktori yang benar.', + '< Previous' => '< Sebelumnya', + '<< First' => '<< Pertama', + 'Active Record requires a "db" CDbConnection application component.' => 'Rekaman Aktif memerlukan sebuah komponen aplikasi CDbConnection "db".', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Rekaman aktif "{class}" memiliki konfigurasi relasi "{relation}" yang salah. Ia harus menetapkan tipe, kelas rekaman aktif terkait dan kunci asing.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Rekaman aktif "{class}" mencoba untuk memilih kolom "{column}" yang tidak benar. Catatan, kolom harus ada dalam tabel atau merupakan sebuah ekspresi dengan alias.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" tidak benar. Pastikan ia merujuk ke direktori atau file yang sudah ada.', + 'Application base path "{path}" is not a valid directory.' => 'Path basis aplikasi "{path}" bukanlah direktori yang benar.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Path runtime aplikasi "{path}" tidak benar. Pastikan direktori bisa ditulis oleh proses server Web.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Item otorisasi "{item}" sudah ditempatkan kepada pengguna "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache memerlukan ekstensi APC PHP sudah aktif di server.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" tidak benar. Pastikan direktori sudah ada dan bisa ditulis oleh proses server Web.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID tidak benar. Pastikan "{id}" merujuk ke cache komponen aplikasi yang benar.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" tidak benar. Tidak bisa menemukan aksi tersebut dalam kontroler saat ini.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" tidak benar. Pastikan ia merujuk ke ID komponen aplikasi CDbConnection.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" tidak benar. Pastikan ia merujuk ke ID komponen aplikasi CDbConnection.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql tidak boleh kosong.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand gagal menjalankan pernyataan SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand gagal menyiapkan pernyataan SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection tidak mendukung pembacaan skema untuk database {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection gagal membuka koneksi DB: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection tidak aktif dan tidak bisa melakukan operasi DB apapun.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString tidak boleh kosong.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader tidak bisa mundur. Ini adalah pembaca hanya-maju.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" tidak benar. Pastikan ia merujuk ke ID komponen aplikasi CDbConnection.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute memerlukan tabel database "{table}" untuk menyimpan catatan pesan.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" tidak merujuk ke komponen aplikasi CDbConnection yang benar.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID tidak benar. Pastikan "{id}" merujuk ke komponen aplikasi database yang benar.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction tidak aktif dan tidak bisa melakukan commit atau operasi roll back.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory tidak boleh kosong.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName tidak boleh kosong.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" tidak merujuk ke direktori yang benar. Pastikan direktori ada dan bisa ditulis oleh proses server Web.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain hanya bisa mengambil obyek yang mengimplementasikan antar muka IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl tidak boleh kosong.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name tidak boleh kosong.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName tidak boleh kosong.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection hanya bisa menampung obyek CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest tidak bisa menentukan URL naskah entri.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest tidak bisa menentukan info path permintaan.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest tidak bisa menentukan URI yang diminta.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode hanya bisa berupa "none", "allow" atau "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" tidak benar. Ia harus berupa integer antara 0 dan 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" bukan direktori yang benar.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache memerlukan ekstensi PHP memcache sudah dipanggil.', + 'CMemCache server configuration must be an array.' => 'Konfigurasi server CMemCache harus berupa array.', + 'CMemCache server configuration must have "host" value.' => 'Konfigurasi server CMemCache harus memiliki nilai "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name diperlukan', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute menemukan blok kode tidak sama "{token}". Pastikan panggilan Yii::beginProfile() dan Yii::endProfile() diulang dengan benar.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" tidak benar. Nilai yang benar termasuk "summary" dan "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager memerlukan ekstensi PHP mcrypt dipanggil agar bisa menggunakan fitur enkripsi data.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey tidak boleh kosong.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation harus berupa "MD5" atau "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey tidak boleh kosong.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> hanya bisa menampung obyek kelas {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat harus "path" atau "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache memerlukan ekstensi PHP XCache sudah dipanggil.', + 'Cache table "{tableName}" does not exist.' => 'Tabel cache "{tableName}" tidak ada.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Tidak bisa menambah "{child}" sebagai anak dari "{name}". Pengulangan terdeteksi.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Tidak bisa menambah "{child}" sebagai anak dari "{parent}". Pengulangan terdeteksi.', + 'Cannot add "{name}" as a child of itself.' => 'Tidak bisa menambah "{name}" sebagai anak dirinya sendiri.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Tidak bisa menambah item jenis "{child}" ke item jenis "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => 'Baik "{parent}" atau "{child}" tidak ada.', + 'Error: Table "{table}" does not have a primary key.' => 'Salah: Tabel "{table}" tidak memiliki kunci primer.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Salah: Tabel "{table}" memiliki kunci primer composite yang tidak didukung oleh perintah crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Event "{class}.{event}" dilampirkan dengan pengendali "{handler}" yang tidak benar.', + 'Event "{class}.{event}" is not defined.' => 'Event "{class}.{event}" tidak didefinisikan.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Gagal menulis file yang di-upload "{file}" ke disk.', + 'File upload was stopped by extension.' => 'Upload file dihentikan oleh ekstensi.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filter "{filter}" tidak benar. Kontroler "{class}" tidak memiliki metode filter "filter{filter}".', + 'Get a new code' => 'Ambil kode baru', + 'Go to page: ' => 'Pergi ke halaman: ', + 'Invalid MO file revision: {revision}.' => 'Revisi file MO: {revision} tidak benar.', + 'Invalid MO file: {file} (magic: {magic}).' => 'File MO tidak benar: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Nilai enumerable "{value}" tidak benar. Pastikan ia ada diantara ({enum}).', + 'Last >>' => 'Terakhir >>', + 'List data must be an array or an object implementing Traversable.' => 'Data daftar harus berupa array atau obyek yang mengimplementasi Traversable.', + 'List index "{index}" is out of bound.' => 'Indkes daftar "{index}" di luar jangkauan.', + 'Login Required' => 'Harus Masuk', + 'Map data must be an array or an object implementing Traversable.' => 'Data Peta harus berupa array atau obyek yang mengimplementasi Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Folder temporal tidak ada untuk menyimpan file "{file}" yang di-upload.', + 'Next >' => 'Berikutnya >', + 'No columns are being updated for table "{table}".' => 'Tidak ada kolom yang dimutakhirkan untuk tabel "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Tidak ada kolom penghitung yang dimutakhirkan untuk tabel "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Konfigurasi object harus berupa array yang berisi elemen "class".', + 'Please fix the following input errors:' => 'Silahkan betulkan kesalahan input berikut:', + 'Property "{class}.{property}" is not defined.' => 'Properti "{class}.{property}" tidak didefinisikan.', + 'Property "{class}.{property}" is read only.' => 'Properti "{class}.{property}" hanya dibaca.', + 'Queue data must be an array or an object implementing Traversable.' => 'Data Queue harus harus berupa array atau obyek yang mengimplementasi Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relasi "{name}" tidak didefinisikan dalam kelas rekaman aktif "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Data stack harus berupa array atau obyek yang mengimplementasi Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabel "{table}" tidak memiliki kolom bernama "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Tabel "{table}" tidak memiliki kunci primer yang didefinisikan.', + 'The "filter" property must be specified with a valid callback.' => 'Properti "filter" harus ditetapkan dengan callback yang benar.', + 'The "pattern" property must be specified with a valid regular expression.' => 'Properti "pattern" harus ditetapkan dengan ekspresi reguler yang benar.', + 'The "view" property is required.' => 'Properti "view" dibutuhkan.', + 'The CSRF token could not be verified.' => 'Token CSRF tidak bisa diverifikasi.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Pola URL "{pattern}" untuk rute "{route}" bukan ekspresi reguler yang benar.', + 'The active record cannot be deleted because it is new.' => 'Rekaman aktif tidak bisa dihapus karena ia baru.', + 'The active record cannot be inserted to database because it is not new.' => 'Rekaman aktif tidak bisa disisipkan ke database karena ia bukan yang baru.', + 'The active record cannot be updated because it is new.' => 'Rekaman aktif tidak bisa dimutakhirkan karena ia baru.', + 'The asset "{asset}" to be published does not exist.' => 'Asset "{asset}" yang dipublikasikan tidak ada.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Kolom "{column}" bukan kunci asing dalam tabel "{table}".', + 'The command path "{path}" is not a valid directory.' => 'Path perintah "{path}" bukan direktori yang benar.', + 'The controller path "{path}" is not a valid directory.' => 'Path kontroler "{path}" bukan direktori yang benar.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'File "{file}" tidak bisa di-upload. Hanya file dengan ekstensi ini yang diijinkan: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'File "{file}" terlalu besar. Ukurannya melebihi {limit} byte.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'File "{file}" terlalu kecil. Ukurannya tidak bisa lebih kecil dari {limit} byte.', + 'The file "{file}" was only partially uploaded.' => 'File "{file}" hanya sebagian yang di-upload.', + 'The first element in a filter configuration must be the filter class.' => 'Elemen pertama dalam konfigurasi filter harus kelas filter.', + 'The item "{name}" does not exist.' => 'Item "{name}" tidak ada.', + 'The item "{parent}" already has a child "{child}".' => 'Item "{parent}" sudah memiliki anak "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Path tata letak "{path}" bukan direktori yang benar.', + 'The list is read only.' => 'Daftar hanya dibaca.', + 'The map is read only.' => 'Peta hanya dibaca.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Pola untuk format 12 jam harus "h" atau "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Pola untuk format 24 jam harus "H" atau "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Pola untuk tanda AM/PM harus "a".', + 'The pattern for day in month must be "F".' => 'Pola untuk hari dalam bulan harus "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Pola untuk hari dalam tahun harus "D", "DD" atau "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Pola untuk hari dalam bulan harus "d" atau "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Pola untuk hari dalam minggu harus "E", "EE", "EEE", "EEEE" atau "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Pola untuk jaman harus "G", "GG", "GGG", "GGGG" atau "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Pola untuk jam dalam AM/PM harus "K" atau "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Pola untuk jam dalam hari harus "k" atau "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Pola untuk menit harus "m" atau "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Pola untuk bulan harus "M", "MM", "MMM", atau "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Pola untuk detik harus "s" atau "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Pola untuk zona waktu harus "z" atau "v".', + 'The pattern for week in month must be "W".' => 'Pola untuk minggu dalam bulan harus "W".', + 'The pattern for week in year must be "w".' => 'Pola untuk minggu dalam tahun harus "w".', + 'The queue is empty.' => 'Antrian kosong.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Relasi "{relation}" dalam kelas rekaman aktif "{class}" tidak ditetapkan dengan benar: join tabel "{joinTable}" yang disediakan dalam kunci asing tidak bisa ditemukan di dalam database.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Relasi "{relation}" dalam kelas rekaman aktif "{class}" ditetapkan dengan kunci asing yang tidak lengkap. Kunci asing harus terdiri dari kolom yang mereferensi ke tabel yang digabung.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Relasi "{relation}" dalam kelas rekaman aktif "{class}" ditetapkan dengan kunci asing "{key}" yang tidak benar. Kunci asing tidak merujuk ke tabel yang digabung.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Relasi "{relation}" dalam kelas rekaman aktif "{class}" ditetapkan dengan kunci asing yang tidak benar. Format kunci asing harus "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Kontroler "{controller}" yang diminta tidak ada.', + 'The requested view "{name}" is not found.' => 'Tinjauan "{name}" yang diminta tidak ditemukan.', + 'The stack is empty.' => 'Stack kosong.', + 'The system is unable to find the requested action "{action}".' => 'Sistem tidak bisa menemukan aksi "{action}" seperti yang diminta.', + 'The system view path "{path}" is not a valid directory.' => 'Path tinjauan sistem "{path}" bukan direktori yang benar.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Tabel "{table}" untuk kelas rekaman aktif "{class}" tidak ditemukan dalam database.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Nilai kunci primer "{key}" tidak disediakan saat meng-queri tabel "{table}".', + 'The verification code is incorrect.' => 'Kode verifikasi tidak benar.', + 'The view path "{path}" is not a valid directory.' => 'Path tinjauan "{path}" bukan direktori yang benar.', + 'Theme directory "{directory}" does not exist.' => 'Direktori tema "{directory}" tidak ada.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Konten ini memerlukan <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Tidak bisa menambah item yang namanya sama seperti item yang sudah ada.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Tidak bisa mengubah nama item. Nama "{name}" sudah dipakai oleh item lain.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Tidak bisa membuat file kondisi aplikasi "{file}". Pastikan direktori berisi file yang sudah ada dan bisa ditulis oleh proses server Web.', + 'Unable to find the decorator view "{view}".' => 'Tidak bisa menemukan dekorator view "{view}".', + 'Unable to lock file "{file}" for reading.' => 'Tidak bisa mengunci file "{file}" untuk pembacaan.', + 'Unable to lock file "{file}" for writing.' => 'Tidak bisa mengunci file "{file}" untuk penulisan.', + 'Unable to read file "{file}".' => 'Tidak bisa membaca file "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Tidak bisa memainkan aksi "{object}.{method}". Metode tidak ada.', + 'Unable to write file "{file}".' => 'Tidak bisa menulis file "{file}".', + 'Unknown authorization item "{name}".' => 'Item otorisasi "{name}" tidak dikenal.', + 'Unrecognized locale "{locale}".' => 'Lokal "{locale}" tidak dikenal.', + 'View file "{file}" does not exist.' => 'File tinjauan "{file}" tidak ada.', + 'Yii application can only be created once.' => 'Aplikasi Yii hanya bisa dibuat satu kali.', + 'You are not authorized to perform this action.' => 'Anda tidak diotorisasi untuk melakukan aksi ini.', + 'Your request is not valid.' => 'Permintaan Anda tidak benar.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" sudah dipakai.', + '{attribute} cannot be blank.' => '{attribute} tidak boleh kosong.', + '{attribute} is invalid.' => '{attribute} tidak benar.', + '{attribute} is not a valid URL.' => '{attribute} bukan URL yang benar.', + '{attribute} is not a valid email address.' => '{attribute} bukan alamat email yang benar.', + '{attribute} is not in the list.' => '{attribute} tidak di dalam daftar.', + '{attribute} is of the wrong length (should be {length} characters).' => 'Panjang {attribute} salah (seharusnya {length} karakter).', + '{attribute} is too big (maximum is {max}).' => '{attribute} terlalu besar (maksimum {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} terlalu panjang (maksimum {max} karakter).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} terlalu pendek (minimum {min} karakter).', + '{attribute} is too small (minimum is {min}).' => '{attribute} terlalu kecil (minimum {min}).', + '{attribute} must be a number.' => '{attribute} harus berupa angka.', + '{attribute} must be an integer.' => '{attribute} harus berupa integer.', + '{attribute} must be repeated exactly.' => '{attribute} harus diulang secara tepat.', + '{attribute} must be {type}.' => '{attribute} haruslah {type}.', + '{className} does not support add() functionality.' => '{className} tidak mendukung fungsionalitas add().', + '{className} does not support delete() functionality.' => '{className} tidak mendukung fungsionalitas delete().', + '{className} does not support flush() functionality.' => '{className} tidak mendukung fungsionalitas flush().', + '{className} does not support get() functionality.' => '{className} tidak mendukung fungsionalitas get().', + '{className} does not support set() functionality.' => '{className} tidak mendukung fungsionalitas set().', + '{class} does not have a method named "{name}".' => '{class} tidak memiliki metode bernama "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} tidak memiliki atribut "{attribute}".', + '{class} does not have attribute "{name}".' => '{class} tidak memiliki atribut "{name}".', + '{class} does not have relation "{name}".' => '{class} tidak memiliki relasi "{name}".', + '{class} does not support fetching all table names.' => '{class} tidak mendukung pengambilan semua nama tabel.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} memiliki aturan validasi yang tidak benar. Aturan harus menetapkan atribut untuk divalidasi dan nama validatornya.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} harus menetapkan nilai "model" dan "attribute" atau properti "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin harus disetel true untuk menggunakan otentikasi berbasis-cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() harus diimplementasikan.', + '{controller} cannot find the requested view "{view}".' => '{controller} tidak bisa menemukan tinjauan "{view}" yang diminta.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} berisi tag widget berulang yang tidak benar dalam tinjauannya "{view}". Widget {widget} tidak memiliki panggilan endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} memiliki panggilan endWidget({id}) ekstra dalam tinjauannya.', + '{widget} cannot find the view "{view}".' => '{widget} tidak menemukan view "{view}".', +); diff --git a/web/framework-1.0.8/messages/it/yii.php b/web/framework-1.0.8/messages/it/yii.php new file mode 100644 index 0000000..a4dc5d4 --- /dev/null +++ b/web/framework-1.0.8/messages/it/yii.php @@ -0,0 +1,208 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest non può determinare le informazioni sul path della richiesta.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest non può determinare l\'URI richiesto.', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache richiede che l\'estensione XCache di PHP sia caricata.', + 'Cannot add "{name}" as a child of itself.' => 'Non si può aggiungere "{name}" come figlio di se stesso.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'La colonna "{column}" non è una chiave esterna nella tabella "{table}".', + '{class} does not have attribute "{attribute}".' => '{class} non ha un attributo "{attribute}".', + '{class} does not have relation "{name}".' => '{class} non ha una relazione "{name}".', + '{class} does not support fetching all table names.' => '{class} non supporta l\'ottenimento di tutti i nomi delle tabelle.', + '"{path}" is not a valid directory.' => '"{path}" non è una cartella valida.', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record richiede un componente applicativo "db" di tipo CDbConnection.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'L\'active record "{class}" ha una configurazione non valida per la relazione "{relation}". Devono essere specificati il tipo di relazione, l\'active record relazionato e la chiave esterna.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" sta cercando di selezionare la colonna non valida "{column}". Nota, la colonna deve esistere nella tabella o essere un\'espressione con alias.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'L\'alias "{alias}" non è valido. Verificare che punti ad una cartella o file esistente.', + 'Application base path "{path}" is not a valid directory.' => 'Il percorso base dell\'applicazione "{path}" non è una cartella valida.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Il percorso di runtime dell\'applicazione "{path}" non è valido. Verificare che sia una cartella con permesso di scrittura per il processo del Web server.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'L\'elemento di autorizzazione "{item}" è stato assegnato all\'utente "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache richiede che l\'estensione apc di PHP sia caricata.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" non è valido. Verificare che la cartella esista e abbia permesso di scrittura per il processo del Web server.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID non è valido. Verifica che "{id}" si riferisca ad un componente applicativo cache valido.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" non è valido. Non si è potuto trovare tale azione nell\'attuale controller.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" non è valido. Verificare che si riferisca all\'ID di un componente applicativo CDbConnection.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" non valido. Verificare che si riferisca all\'ID di un componente applicativo CDbConnection.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql non può essere vuoto.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand ha riportato un errore nell\'esecuzione della query SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand ha riportato un errore nella preparazione della query SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection non supporta la lettura dello schema per DB {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection ha riportato un errore nell\'apertura della connessione al DB: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection è inattiva e non può realizzare alcuna operazione con il DB.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString non può essere vuoto', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader non può tornare indietro. E\' un lettore che può solo avanzare.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" non valido. Verificare che si riferisca all\'ID di un componente applicativo CDbConnection', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute richiede la tabella "{table}" del DB per salvare i messaggi di log.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" non punta ad un componente applicativo CDbConnection valido.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID non è valido. Verificare che "{id}" si riferisca ad un componente applicativo DB valido.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction è inattiva e non può realizzare le operazioni di commit e roll back.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory non può essere vuoto.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName non può essere vuoto.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" no punta ad una cartella valida. Verificare che la cartella esista e abbia permesso di scrittura per il processo del Web server.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain può solamente prendere oggetti che implementino l\'interfaccia IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl non può essere vuoto.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name non può essere vuoto.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName non può essere vuoto.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection può contenere solo oggetti CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest non può determinare l\'URL dello script di entry.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode può essere solo "none", "allow" o "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" non è valido. Deve essere un intero compreso tra 0 e 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" non è una cartella valida.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache richiede che l\'estensione memcache di PHP sia caricata.', + 'CMemCache server configuration must be an array.' => 'La configurazione del server CMemCache deve essere un array.', + 'CMemCache server configuration must have "host" value.' => 'La configurazione del server CMemCache deve specificare un valore "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name è richiesto', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute ha trovato un blocco di codice "{token}" disallineato. Verificare che le chiamate a Yii::beginProfile() e a Yii::endProfile() siano correttamente annidate.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" non è valido. I valori validi includono "summary" e "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager richiede che l\'estensione mcrypt di PHP sia caricata per utilizzare l\'opzione di crittografia dei dati.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey non può essere vuoto.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation deve essere "MD5" o "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey non può essere vuoto.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> può contenere solo oggetti della classe {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat deve essere "path" o "get".', + 'Cache table "{tableName}" does not exist.' => 'La tabella di cache "{tableName}" non esiste.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Non si può aggiungere "{child}" come figlio di "{name}". E\' stato individuato un ciclo.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Non si può aggiungere "{child}" come figlio di "{parent}". E\' stato individuato un ciclo.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Non si può aggiungere un elemento di tipo "{child}" ad uno di tipo "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" o "{child}" non esistono', + 'Error: Table "{table}" does not have a primary key.' => 'Errore: La tabella "{table}" non ha una chiave primaria.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Errore: La tabella "{table}" non ha una chiave primaria composta che non è supportata dal comando crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'L\'evento "{class}"."{event}" è associato ad un handler "{handler}" non valido.', + 'Event "{class}.{event}" is not defined.' => 'L\'evento "{class}"."{event}" non è definito.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Fallimento nella scrittura su disco del file caricato "{file}".', + 'File upload was stopped by extension.' => 'Carimento del file "{file}" interrotto a causa dell\'estensione.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'El filtro "{filter} non è velido. Il controller "{class}" non contiene il metodo di filter "filter{filter}".', + 'Get a new code' => 'Procurasi un codice nuovo', + 'Invalid MO file revision: {revision}.' => 'Revisione del file MO non valida: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'File MO non valido: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Valore enumerabile non valido "{value}". Verificare che sia entro ({enum}).', + 'List data must be an array or an object implementing Traversable.' => 'I dati della lista devono essere un array o un oggetto che implementi Traversable.', + 'List index "{index}" is out of bound.' => 'L\'indice della lista "{index}" è fuori dai limiti.', + 'Login Required' => 'Login richiesto.', + 'Map data must be an array or an object implementing Traversable.' => 'I dati di map devono essere un array o un oggetto che implementi Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Impossibile trovare la cartella temporanea per effettuare il carimento del file "{file}".', + 'No columns are being updated for table "{table}".' => 'Nessuna colonna è stata aggiornata per la tabella "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Nessuna contatore di colonna è stata aggiornata per la tabella "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'La configurazione dell\'oggetto deve essere un array contenente un elemento "class".', + 'Please fix the following input errors:' => 'Per favore, correggere i seguenti errori di input:', + 'Property "{class}.{property}" is not defined.' => 'La proprietà "{class}"."{property}" non è definita.', + 'Property "{class}.{property}" is read only.' => 'La proprietà "{class}"."{property}" è di sola lettura.', + 'Queue data must be an array or an object implementing Traversable.' => 'I dati della coda devono essere un array o un oggetto che implementi Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'La relazione "{name}" non è definita nella classe active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'I dati della pila devono essere un array o un oggetto che implementi Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'La tabella "{table}" non contiene la colonna "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'La tabella "{table}" non ha definita nessuna chiave primaria.', + 'The "filter" property must be specified with a valid callback.' => 'La proprietà "filter" deve essere specificata con un callback valido.', + 'The "pattern" property must be specified with a valid regular expression.' => 'La proprietà "pattern" deve essere specificata come una espressione regolare valida.', + 'The "view" property is required.' => 'La proprietà "view" è richiesta.', + 'The CSRF token could not be verified.' => 'Il token CSRF non può essere verificato.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Il formato dell\'URL "{pattern}" per il percorso "{route}" non è un\'espressione regolare valida.', + 'The active record cannot be deleted because it is new.' => 'L\'active record non può essere eliminato perché è nuovo.', + 'The active record cannot be inserted to database because it is not new.' => 'L\'active record non può essere inserito nel DB perché non è nuovo.', + 'The active record cannot be updated because it is new.' => 'L\'active record non può essere aggiornato perché è nuovo.', + 'The asset "{asset}" to be pulished does not exist.' => 'El asset "{asset} che deve essere pubblicato non esiste.', + 'The command path "{path}" is not a valid directory.' => 'Il percorso del comando "{path}" non è una cartella valida.', + 'The controller path "{path}" is not a valid directory.' => 'Il percorso del controller "{path}" non è una cartella valida.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Il caricamento del file "{file}" non può essere effettuato. Solo i file con le seguenti estensioni sono permessi: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Il file "{file}" è troppo grande. La dimensione non può superare {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Il file "{file}" è troppo piccol. La dimensione deve essere di almeno {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'Il file "{file}" è stato caricato parzialmente.', + 'The first element in a filter configuration must be the filter class.' => 'Il primo elemento nella configurazione di un filtro deve essere la classe del filtro.', + 'The item "{name}" does not exist.' => 'L\'elemento "{name}" non esiste.', + 'The item "{parent}" already has a child "{child}".' => 'L\'elemento "{parent}" contiene già un figlio "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Il percorso del layout "{path}" non è una cartella valida.', + 'The list is read only.' => 'La lista è di sola lettura', + 'The map is read only.' => 'Il map è di sola lettura', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Il formato per le 12 ore deve essere "h" ó "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Il formato per le 24 ore deve essere "H" ó "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Il formato per l\'indicatore AM/PM deve essere "a".', + 'The pattern for day in month must be "F".' => 'Il formato per il giorno nel mese deve essere "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Il formato per il giorno nell\'anno deve essere "D", "DD", "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Il formato per il giorno del mese deve essere "d" o "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Il formato per il giorno della settimana deve essere "E", "EE", "EEE", "EEEE" ó "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Il formato per l\'era deve essere "G", "GG", "GGG", "GGGG" ó "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Il formato per l\'ora in AM/PM deve essere "K" ó "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Il formato per l\'ore nel giorno deve essere "k" ó "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Il formato per i minuti deve essere "m" ó "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Il formato per il mese deve essere "M", "MM", "MMM" o "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Il formato per i secondi deve essere "s" ó "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Il formato per la timezone deve essere "z" ó "v".', + 'The pattern for week in month must be "W".' => 'Il formato per la settimana nel mese deve essere "W".', + 'The pattern for week in year must be "w".' => 'Il formato per la settimana nell\'anno deve essere "w".', + 'The queue is empty.' => 'La coda è vuota', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'La relazione "{relation}" nella classe active record "{class}" non è specificata correttamente: la tabla di join "{join table}" specificata nella chiave esterna non può essere trovata nel DB.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'La relazione "{relation}" nella classe active record "{class}" è specificata con una chiave esterna non completa. La chiave esterna deve consistere nelle colonne che referenziano entrambe le tabelle di join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'La relazione "{relation}" nella classe active record "{class}" è specificata con una chiave esterna non valida "{key}". La chiave esterna non punta a nessuna tabella di join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'La relazione "{relation}" nella classe active record "{class}" è specificata con una chiave esterna non valida. Il formato della chiave esterna deve essere "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Il controller "{controller}" richiesto non esiste.', + 'The requested view "{name}" is not found.' => 'Impossibile trovare la vista "{name}" richiesta.', + 'The stack is empty.' => 'La pila è vuota', + 'The system is unable to find the requested action "{action}".' => 'Il sistema non ha potuto trovare l\'azione "{action}" richiesta.', + 'The system view path "{path}" is not a valid directory.' => 'Il percorso della vista di sistema "{path}" non è una cartella valida.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'La tabella "{table}" definita nella classe active record "{class}" non è presente nel DB.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Il valore della chiave primaria "{key}" non è stato fornito per interrogare la tabella "{table}".', + 'The verification code is incorrect.' => 'Il codice di verifica non è corretto.', + 'The view path "{path}" is not a valid directory.' => 'Il percorso della vista "{path}" non è una cartella valida.', + 'Theme directory "{directory}" does not exist.' => 'La cartella dei temi "{directory}" non esiste.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Il contenuto richiede <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Non si può aggiungere un elemento il cui nome sia lo stesso di quello di un altro elemento.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Impossibile cambiare il nome dell\'elemento. Il nome "{name}" è già utilizzato da un altro elemento.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Impossibile creare il file di stato dell\'applicazione "{file}". Verificare che la cartella che contiene il file esista e abbia permesso di scrittura per il processo del Web server.', + 'Unable to find the decorator view "{view}".' => 'Impossibile trovare la vista del decoratore "{view}".', + 'Unable to find the list item.' => 'Impossibile trovare l\'item della lista.', + 'Unable to lock file "{file}" for reading.' => 'Impossibile avere il lock sul file "{file}" per la lettura.', + 'Unable to lock file "{file}" for writing.' => 'Impossibile avere il lock sul "{file}" per la scrittura.', + 'Unable to read file "{file}".' => 'Impossibile leggere il file "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Impossibile replicare l\'azione "{object}.{method}". Il metodo è inesistente.', + 'Unable to write file "{file}".' => 'Impossibile scrivere il file "{file}".', + 'Unknown authorization item "{name}".' => 'Elemento di autorizzazione "{name}" sconosciuto.', + 'Unrecognized locale "{locale}".' => 'Localizzazione non riconosciuta "{locale}".', + 'View file "{file}" does not exist.' => 'Il file della vista "{view}" non esiste.', + 'Yii application can only be created once.' => 'Si può creare una sola applicazione Yii.', + 'You are not authorized to perform this action.' => 'Non si ha l\'autorizzazione ad effettuare l\'operazione.', + 'Your request is not valid.' => 'La richiesta non è valida.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" è già stato preso.', + '{attribute} cannot be blank.' => '{attribute} non può essere nullo.', + '{attribute} is invalid.' => '{attribute} non è valido.', + '{attribute} is not a valid URL.' => '{attribute} non è un URL valido.', + '{attribute} is not a valid email address.' => '{attribute} non è una email valida.', + '{attribute} is not in the list.' => '{attribute} non è nella lista.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} è della lunghezza sbagliata (deve essere di {length} caratteri)', + '{attribute} is too big (maximum is {max}).' => '{attribute} è troppo grande (il massimo è {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} è troppo lungo (il massimo è di {max} caratteri)', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} è troppo corto (il minimo è di {min} caratteri)', + '{attribute} is too small (minimum is {min}).' => '{attribute} è troppo piccolo (il minimo è {min}).', + '{attribute} must be a number.' => '{attribute} deve essere un numero.', + '{attribute} must be an integer.' => '{attribute} deve essere un intero.', + '{attribute} must be repeated exactly.' => '{attribute} deve essere ripetuto esattamente.', + '{attribute} must be {type}.' => '{attribute} deve essere {type}.', + '{className} does not support add() functionality.' => '{className} non supporta la funzionalità add().', + '{className} does not support delete() functionality.' => '{className} non supporta la funzionalità delete().', + '{className} does not support flush() functionality.' => '{className} non supporta la funzionalità flush().', + '{className} does not support get() functionality.' => '{className} non supporta la funzionalità get().', + '{className} does not support set() functionality.' => '{className} non supporta la funzionalità set().', + '{class} does not have attribute "{name}".' => '{class} non contiene l\'attributo "{name}".', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} ha una regola di validazione non valida. La regola deve specificare gli attributi che devono essere validati e il nome del validatore.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} deve specificare i valori delle proprietà "model" e "attribute" oppure "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin deve essere impostato a true per utilizzare l\'autenticazione basata su cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() deve essere implementata.', + '{controller} cannot find the requested view "{view}".' => '{controller} non ha potuto trovare la vista "{view}" richiesta.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} contiene tag nella vista "{view}" non correttamente annidati. {widget} widget non contiene la chiamata a endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} contiene una chiamata extra a endWidget({id}) all\'interno della sua vista.', + '{widget} cannot find the view "{view}".' => '{widget} non trova la vista "{view}".', +); diff --git a/web/framework-1.0.8/messages/ja/yii.php b/web/framework-1.0.8/messages/ja/yii.php new file mode 100644 index 0000000..2f2f376 --- /dev/null +++ b/web/framework-1.0.8/messages/ja/yii.php @@ -0,0 +1,230 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '< Previous' => '< 前', + '<< First' => '<< 最初', + 'Base path "{path}" is not a valid directory.' => 'ベースパス"{path}"は正しいディレクトリではありません。', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => 'CEAcceleratorCache はPHPの eAccelerator 拡張を必要とします。有効にするか、 "--with-eaccelerator-shared-memory" オプションをつけてコンパイルしてください。', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => 'CZendDataCache は PHP Zend Data Cache 拡張を必要とします。', + 'Column name must be either a string or an array.' => 'カラム名は文字列または配列でなければいけません。', + 'Go to page: ' => 'ページ:', + 'Last >>' => '最後 >>', + 'Next >' => '次 >', + 'Table "{table}" does not exist.' => 'テーブル"{table}"は存在しません。', + 'The STAT relation "{name}" cannot have child relations.' => 'STATリレーション"{name}"は子リレーションを持つことができません。', + 'The asset "{asset}" to be published does not exist.' => 'アセット"{asset}"は存在しません。', + 'The module path "{path}" is not a valid directory.' => 'モジュールパス{path}は正しいディレクトリではありません。', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'アクティブレコードクラス"{class}"のリレーション"{relation}"は正しく設定されていません。外部キーで与えられるジョインテーブル"{joinTable}"はデータベースに存在しません。', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => 'アクティブレコードクラス"{class}"のリレーション"{relation}"は外部キー"{key}"で定義されていますが、親テーブル"{table}"を示していません。', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => 'アクティブレコードクラス"{class}"のリレーション"{relation}"は不正な外部キー"{key}"で定義されています。テーブル"{table}"にはそのようなカラムは存在しません。', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => 'アクティブレコードクラス"{class}"のリレーション"{relation}"は不正な外部キー"{key}"で定義されています。キー中のカラムはテーブル"{table}"にのプライマリキーと一致しなければなりません。', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => 'テーブル"{table}"のクエリについて、カラム"{column}"の値が設定されていません。', + 'Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.' => '複合プライマリキーのレコードをカウントできません。クエリ条件のSELECTオプションに明示してください。', + 'Unable to resolve the request "{route}".' => 'リクエスト"{route}"を解決できません。', + '{attribute} "{value}" is invalid.' => '{attribute}の値"{value}" は正しくありません。', + '{class} does not have a method named "{name}".' => 'クラス{class}には"{name}"という名のメソッドは存在しません。', + 'Table "{table}" does not have a primary key defined.' => '@@テーブル "{table}" にはプライマリキーが存在しません。@@', + 'The "view" property is required.' => '@@"view" プロパティが必要です。@@', + 'The asset "{asset}" to be pulished does not exist.' => '@@アセット "{asset}" が存在しません。@@', + 'The column "{column}" is not a foreign key in table "{table}".' => '@@カラム "{column}" はテーブル "{table}" の外部キーではありません。@@', + 'The requested controller "{controller}" does not exist.' => '@@要求されたコントローラ "{controller}" は存在しません。@@', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '@@テーブル "{table}" のクエリにおいて、プライマリキー "{key}" の値が指定されていません。@@', + 'Unable to find the decorator view "{view}".' => '@@デコレータビュー "{view}" が見つかりません。@@', + 'Unable to find the list item.' => '@@リストアイテムが見つかりません。@@', + '"{path}" is not a valid directory.' => '{path} は正しいディレクトリではありません。', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record は "db" という CDbConnection アプリケーションコンポーネントを必要とします。', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'アクティブレコード "{class}" の、リレーション {relation} のコンフィギュレーションに誤りがあります。リレーションタイプ、関係するアクティブレコードクラスと外部キーを指定して下さい。', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'アクティブレコード "{class}" は誤ったカラム "{column}" を選択しようとしました。カラムがテーブルにあるか、または別名として正しいかを確認してください。', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '別名 "{alias}" が間違っています。存在するディレクトリやファイルを指しているかを確認してください。', + 'Application base path "{path}" is not a valid directory.' => 'アプリケーションベースパス "{path}" は正しいディレクトリではありません。', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'アプリケーション実行時パス "{path}" は正しくありません。ウェブサーバプロセスが書き込めるかを確認してください。', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '認証項目 "{item}" は既にユーザ "{user}" に割り当てられています。', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache はPHPのAPC拡張を必要とします。', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" は不正です。ディレクトリが存在してウェブサーバプロセスが書き込めるかを確認してください。', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID が不正です。"{id}" が正しいキャッシュアプリケーションコンポーネントを参照しているか確認してください。', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" が不正です。現在のコントローラ中にそのようなアクションが存在しません。', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" が不正です。CDbConnection アプリケーションコンポーネントのIDを参照しているかを確認してください。', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" が不正です。CDbConnection アプリケーションコンポーネントのIDを参照しているかを確認してください。', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql は空白ではいけません。', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand はSQL文を実行できません。:{error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand はSQL文を準備できません。:{error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection は {driver} データベースに対して読み取りスキーマをサポートしていません。', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection はデータベースコネクションをオープンできません。:{error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection がアクティブでないためデータベース操作ができません。', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString は空白ではいけません。', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader は巻き戻しできません。これは順方向のみのリーダです。', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" が不正です。CDbConnection アプリケーションコンポーネントのIDを参照しているかを確認してください。', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute はログメッセージを格納するためのデータベーステーブル "{table}" を必要とします。', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" が正しい CDbConnection アプリケーションコンポーネントを参照していません', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID が不正です。"{id}"が正しいデータベースアプリケーションコンポーネントを参照しているかを確認してください。', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction がアクティブではなく、コミットやロールバック操作を実行できません。', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory は空白ではいけません。', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName は空白ではいけません。', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" が正しいディレクトリではありません。ディレクトリが存在してウェブサーバプロセスが書き込めるかを確認してください。', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain は IFilter インタフェースを実装したオブジェクトのみ受け取ります。', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl は空白ではいけません。', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name は空白ではいけません。', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName は空白ではいけません。', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection は CHttpCookie オブジェクトのみを持ちます。', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest はエントリスクリプトのURLを決定できません。', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest はリクエストのパス情報を決定できません。', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest はリクエストURIを決定できません。', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode の取る値は"none"か"allow"か又は"only"です。', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" が不正です。0から100までの整数にしてください。', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" は不正なディレクトリです。', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache はPHPのmemcache拡張を必要とします。', + 'CMemCache server configuration must be an array.' => 'CMemCache サーバコンフィギュレーションは配列にしてください。', + 'CMemCache server configuration must have "host" value.' => 'CMemCache サーバコンフィギュレーションには host 値が必要です。', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name が必要です。', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute がコードブロック "{token}" を見つけました。Yii::beginProfile() と Yii::endProfile()の呼び出しが正しくネストされているか確認してください。', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" が不正です。正しい値は "summary" と "callstack" を含みます。', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'データ暗号化機能を使用するためには CSecurityManager はPHPのmcrypt拡張を必要とします。', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey は空白ではいけません。', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation は "MD5" または "SHA1" のいずれかです。', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey は空白ではいけません。', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> は {type} クラスのオブジェクトしか持てません。', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat は "path" か "get" のいずれかです。', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache はPHPのXCache拡張を必要とします。', + 'Cache table "{tableName}" does not exist.' => 'キャッシュテーブル "{tableName}" が存在しません。', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '"{name}" の子供として "{child}" は追加できません。ループが検出されました。', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '"{parent}" の子供として "{child}" は追加できません。ループが検出されました。', + 'Cannot add "{name}" as a child of itself.' => '"{child}" の子供として自分自身は追加できません。', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '"{parent}" 型の項目に "{child}" 型の項目は追加できません。', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" もしくは "{child}" が存在しません。', + 'Error: Table "{table}" does not have a primary key.' => 'エラー: テーブル "{table}" にプライマリキーが存在しません。', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'エラー: テーブル "{table}" に複合プライマリキーが存在しますが、これはCRUDコマンドでサポートされません。', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'イベント "{class}.{event}" に不正なハンドラ "{handler}" がアタッチされました。', + 'Event "{class}.{event}" is not defined.' => 'イベント "{class}.{event}" が定義されていません。', + 'Failed to write the uploaded file "{file}" to disk.' => 'アップロードされたファイル "{file}" がディスクに書けません。', + 'File upload was stopped by extension.' => 'ファイルアップロードが拡張により停止されました。', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'フィルタ "{filter}" が不正です。コントローラ "{class}" はフィルタメソッド "filter{filter}" を持っていません。', + 'Get a new code' => '新しいコードを取得', + 'Invalid MO file revision: {revision}.' => '不正な MO ファイル版数({revision})です。', + 'Invalid MO file: {file} (magic: {magic}).' => '不正な MO ファイル(magic: {magic})です。', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '不正なエニュマラブル値("{value}")です。{enum} の間にあるか確認してください。', + 'List data must be an array or an object implementing Traversable.' => 'リストデータはTraversableを実装したアレイまたはオブジェクトです。', + 'List index "{index}" is out of bound.' => 'リストインデックス "{index}" が範囲を超えました。', + 'Login Required' => 'ログインが必要です。', + 'Map data must be an array or an object implementing Traversable.' => 'マップデータはTraversableを実装したアレイまたはオブジェクトです。', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'アップロードファイル "{file}" をストアする暫定フォルダがありません。', + 'No columns are being updated for table "{table}".' => 'テーブル "{table}" のカラムがどれも変更されていません。', + 'No counter columns are being updated for table "{table}".' => 'テーブル "{table}" のカウンターカラムがどれも変更されていません。', + 'Object configuration must be an array containing a "class" element.' => 'オブジェクトコンフィギュレーションは "class" 要素を含むアレイです。', + 'Please fix the following input errors:' => '以下の入力エラーを修正してください。', + 'Property "{class}.{property}" is not defined.' => 'プロパティ "{class}.{property}" が定義されていません。', + 'Property "{class}.{property}" is read only.' => 'プロパティ "{class}.{property}" はリードオンリです。', + 'Queue data must be an array or an object implementing Traversable.' => 'キューデータはTraversableを実装したアレイまたはオブジェクトです。', + 'Relation "{name}" is not defined in active record class "{class}".' => 'アクティブレコードクラス "{class}" にリレーション "{name}" が定義されていません。', + 'Stack data must be an array or an object implementing Traversable.' => 'スタックデータはTraversableを実装したアレイまたはオブジェクトです。', + 'Table "{table}" does not have a column named "{column}".' => 'テーブル "{table}" には "{column}" というカラムが存在しません。', + 'The "filter" property must be specified with a valid callback.' => '"filter"プロパティには正当なコールバックが定義される必要があります。', + 'The "pattern" property must be specified with a valid regular expression.' => '"pattern" プロパティには正当な正規表現が定義される必要があります。', + 'The CSRF token could not be verified.' => 'CSRFトークンは検証されていません。', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'ルート "{route}" に対するURLパターン "{pattern}" は正当な正規表現ではありません。', + 'The active record cannot be deleted because it is new.' => '新規のためアクティブレコードは削除できません。', + 'The active record cannot be inserted to database because it is not new.' => '新規でないためアクティブレコードはデータベースに挿入できません。', + 'The active record cannot be updated because it is new.' => '新規のためアクティブレコードは修正できません。', + 'The command path "{path}" is not a valid directory.' => 'コマンドパス "{path}" は正当なディレクトリではありません。', + 'The controller path "{path}" is not a valid directory.' => 'コントローラパス "{path}" は正当なディレクトリではありません。', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'ファイル "{file}" がアップロードできません。以下の拡張子を持つファイルだけが認められます。{extensions}', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'ファイル "{file}" が大き過ぎます。サイズは {limit} バイトを超えることができません。', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'ファイル "{file}" が小さ過ぎます。サイズは {limit} バイトを下回ることができません。', + 'The file "{file}" was only partially uploaded.' => 'ファイル "{file}" が部分的にアップロードされました。', + 'The first element in a filter configuration must be the filter class.' => 'フィルタコンフィギュレーションの最初の要素はフィルタクラスです。', + 'The item "{name}" does not exist.' => '要素 "{name}" が存在しません。', + 'The item "{parent}" already has a child "{child}".' => '要素 "{parent}" は既に "{child}" を持っています。', + 'The layout path "{path}" is not a valid directory.' => 'レイアウトパス "{path}" は正当なディレクトリではありません。', + 'The list is read only.' => 'リストは読み出し専用です。', + 'The map is read only.' => 'マップは読み出し専用です。', + 'The pattern for 12 hour format must be "h" or "hh".' => '12時間制フォーマットのパターンは "h" か "hh" です。', + 'The pattern for 24 hour format must be "H" or "HH".' => '24時間制フォーマットのパターンは "H" か "HH" です。', + 'The pattern for AM/PM marker must be "a".' => 'AM/PMのパターンは "a" です。', + 'The pattern for day in month must be "F".' => 'ひと月の何日目のパターンは "F" です。', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '一年の何日目のパターンは "D" か "DD" か "DDD" です。', + 'The pattern for day of the month must be "d" or "dd".' => '日のパターンは "d" か "dd" です。', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '曜日のパターンは "E" か "EE" か "EEE" か "EEEE" か "EEEEE" です。', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '時代のパターンは"G" か "GG" か "GGG" か "GGGG" か "GGGGG" です。', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'AM/PMの時のパターンは "K" か "KK" です。', + 'The pattern for hour in day must be "k" or "kk".' => '一日の時のパターンは "k" か "kk" です。', + 'The pattern for minutes must be "m" or "mm".' => '分のパターンは "m" か "mm" です。', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '月のパターンは "M" か "MM" か "MMM" です。', + 'The pattern for seconds must be "s" or "ss".' => '秒のパターンは "s" か "ss" です。', + 'The pattern for time zone must be "z" or "v".' => 'タイムゾーンのパターンは "z" か "v" です。', + 'The pattern for week in month must be "W".' => 'ひと月の週のパターンは "W" です。', + 'The pattern for week in year must be "w".' => '一年の週のパターンは "w" です。', + 'The queue is empty.' => 'キューが空です。', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'アクティブレコードクラス "{class}" のリレーション "{relation}" は正しく定義されていません。外部キーで与えられるジョインテーブル "{joinTable}" がデータベース中に見付かりません。', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'アクティブレコードクラス "{class}" のリレーション "{relation}" は不完全な外部キーで定義されています。外部キーは両方のジョインテーブルで参照されるカラムで構成されます。', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'アクティブレコードクラス "{class}" のリレーション "{relation}" は不正な外部キー "{key}" で定義されています。外部キーは両方のジョインテーブルのどちらも参照していません。', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'アクティブレコードクラス "{class}" のリレーション "{relation}" は不正な外部キーで定義されています。外部キーのフォーマットは"joinTable(fk1,fk2,...)" です。', + 'The requested view "{name}" is not found.' => '要求されたビュー "{name}" が見つかりません。', + 'The stack is empty.' => 'スタックが空です。', + 'The system is unable to find the requested action "{action}".' => 'システムは要求されたアクション "{action}" を見つけられません。', + 'The system view path "{path}" is not a valid directory.' => 'システムビューパス "{path}" は正当なディレクトリではありません。', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'アクティブレコードクラス "{class}" のテーブル "{table}" がデータベースにありません。', + 'The verification code is incorrect.' => '検証コードが正しくありません。', + 'The view path "{path}" is not a valid directory.' => 'ビューパス "{path}" は正当なディレクトリではありません。', + 'Theme directory "{directory}" does not exist.' => 'テーマディレクトリ "{directory}" が存在しません。', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'このコンテントは <a href="http://www.adobe.com/go/getflash/">アドビフラッシュプレーヤ</a>を必要とします。', + 'Unable to add an item whose name is the same as an existing item.' => '既に存在する項目と同じ名前の項目は追加できません。', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '項目の名前を変更できません。その名前 "{name}" は既に別の項目で使用されています。', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'アプリケーション状態ファイル "{file}" が作成できません。ファイルのディレクトリが存在してウェブサーバプロセスが書き込めるかを確認してください。', + 'Unable to lock file "{file}" for reading.' => 'ファイル "{file}" を読み出しロックできません。', + 'Unable to lock file "{file}" for writing.' => 'ファイル "{file}" を書き込みロックできません。', + 'Unable to read file "{file}".' => 'ファイル "{file}" が読めません。', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'アクション "{object}.{method}" をリプレーできません。メソッドが存在しません。', + 'Unable to write file "{file}".' => 'ファイル "{file}" が書けません。', + 'Unknown authorization item "{name}".' => '認証項目 "{name}" が不明です。', + 'Unrecognized locale "{locale}".' => 'ロケール "{locale}" が認識できません。', + 'View file "{file}" does not exist.' => 'ビューファイル "{file}" が存在しません。', + 'Yii application can only be created once.' => 'Yiiアプリケーションは一度しか作成できません。', + 'You are not authorized to perform this action.' => 'あなたはこのアクションを実行する認証を受けていません。', + 'Your request is not valid.' => 'あなたのリクエストは不正です。', + '{attribute} "{value}" has already been taken.' => '{attribute} の "{value}" は既に得られています。', + '{attribute} cannot be blank.' => '{attribute} は空白ではいけません。', + '{attribute} is invalid.' => '{attribute} は不正です。', + '{attribute} is not a valid URL.' => '{attribute} は正当な URL ではありません。', + '{attribute} is not a valid email address.' => '{attribute} は正当なメールアドレスではありません。', + '{attribute} is not in the list.' => '{attribute} はリストに存在しません。', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} の長さが違っています(ただしくは {length} であるべきです)。', + '{attribute} is too big (maximum is {max}).' => '{attribute} が大き過ぎます(最大 {max})。', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} が長過ぎます(最大 {max} キャラクタ)。', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} が短過ぎます(最小 {max} キャラクタ)。', + '{attribute} is too small (minimum is {min}).' => '{attribute} が小さ過ぎます(最小 {max})。', + '{attribute} must be a number.' => '{attribute} は数字にしてください。', + '{attribute} must be an integer.' => '{attribute} は整数にしてください。', + '{attribute} must be repeated exactly.' => '{attribute} は正確に繰り返してください。', + '{attribute} must be {type}.' => '{attribute} は {type} にしてください。', + '{className} does not support add() functionality.' => '{className} は add() 機能をサポートしません。', + '{className} does not support delete() functionality.' => '{className} は delete() 機能をサポートしません。', + '{className} does not support flush() functionality.' => '{className} は flush() 機能をサポートしません。', + '{className} does not support get() functionality.' => '{className} は get() 機能をサポートしません。', + '{className} does not support set() functionality.' => '{className} は set() 機能をサポートしません。', + '{class} does not have attribute "{attribute}".' => '{class} は "{attribute}" というアトリビュートを持ちません。', + '{class} does not have attribute "{name}".' => '{class} は "{name}" というアトリビュートを持ちません。', + '{class} does not have relation "{name}".' => '{class} は "{name}" というリレーションを持ちません。', + '{class} does not support fetching all table names.' => '{class} はテーブル名を全て取り出す機能はサポートしません。', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} のバリデーションルールは間違っています。ルールには検証するべきアトリビュートとバリデータ名を指定してください。', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} には "model" と、 "attribute" または "name" のプロパティ値を設定してください。', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => 'クッキーベースの認証を使用するには {class}.allowAutoLogin を設定してください。', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() を実装してください。', + '{controller} cannot find the requested view "{view}".' => '{controller} は "{view}" リクエストビューを見つけられません。', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} にはビュー"{view}"の中に不正にネストされたウィジェットタグがあります。{widget} ウィジェットには endWidget() 呼び出しがありません。', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} のビューには余分な endWidget({id}) 呼び出しがあります。', + '{widget} cannot find the view "{view}".' => '{widget} はビュー "{view}" を見つけられません。', +); diff --git a/web/framework-1.0.8/messages/nl/yii.php b/web/framework-1.0.8/messages/nl/yii.php new file mode 100644 index 0000000..8b0bbf0 --- /dev/null +++ b/web/framework-1.0.8/messages/nl/yii.php @@ -0,0 +1,206 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path} is geen geldige map.', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record heeft een "db" CDbConnection applicatie component nodig.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => 'Repareer de volgende fouten:', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be pulished does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => 'Het controller pad "{path}" is geen valide map.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Het bestand "{file}" kan niet worden verstuurd. Alleen de volgende extensies zijn toegestaan: {extensions}', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => 'De rij is leeg.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => 'De aangevraagde controller "{controller}" bestaat niet.', + 'The requested view "{name}" is not found.' => 'De aangevraagde view "{name}" is niet gevonden.', + 'The stack is empty.' => 'De stack is leeg', + 'The system is unable to find the requested action "{action}".' => 'Het systeem kon de aangevraagde actie "{action}" niet vinden.', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => 'De verificatiecode is niet correct.', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => 'Thema map "{directory}" bestaat niet.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to find the list item.' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => '', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => 'De aanvraag is niet valide.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" is al ingenomen.', + '{attribute} cannot be blank.' => '{attribute} mag niet leeg zijn.', + '{attribute} is invalid.' => '{attribute} is niet valide.', + '{attribute} is not a valid URL.' => '{attribute} is geen valide URL.', + '{attribute} is not a valid email address.' => '{attribute} is geen valide emailadres.', + '{attribute} is not in the list.' => '{attribute} zit niet in de lijst.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} is niet de goede lengte (het zou {length} karakters lang moeten zijn)', + '{attribute} is too big (maximum is {max}).' => '{attribute} is te groot (maximum is {max})', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} is te lang (maximum is {max} karakters).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} is te kort (minimum is {min} karakters).', + '{attribute} is too small (minimum is {min}).' => '{attribute} is te klein (minimum is {min}).', + '{attribute} must be a number.' => '{attribute} moet een getal zijn.', + '{attribute} must be an integer.' => '{attribute} moet een geheel getal zijn.', + '{attribute} must be repeated exactly.' => '{attribute} moet exact herhaald worden.', + '{attribute} must be {type}.' => '{attribute} moet zijn {type}', + '{className} does not support add() functionality.' => '{className} ondersteunt geen add() functionaliteit.', + '{className} does not support delete() functionality.' => '{className} ondersteunt geen delete() functionaliteit.', + '{className} does not support flush() functionality.' => '{className} ondersteunt geen flush() functionaliteit.', + '{className} does not support get() functionality.' => '{className} ondersteunt geen get() functionaliteit.', + '{className} does not support set() functionality.' => '{className} ondersteunt geen set() functionaliteit.', + '{class} does not have attribute "{name}".' => '{class} heeft geen attribuut "{name}".', + '{class} does not have relation "{name}".' => '{class} heeft geen relatie "{name}".', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} heeft een extra endWidget ({id}) aanroep in z\'n view', + '{widget} cannot find the view "{view}".' => '{widget} kan het viewbestand "{view}" niet vinden.', +); diff --git a/web/framework-1.0.8/messages/no/yii.php b/web/framework-1.0.8/messages/no/yii.php new file mode 100644 index 0000000..9bdabea --- /dev/null +++ b/web/framework-1.0.8/messages/no/yii.php @@ -0,0 +1,222 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" er ikke en gyldig mappe', + '< Previous' => '< Forrige', + '<< First' => '<< Første', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record krever en "db" CDbConnection applikasjonskomponent.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active record "{class}" har en ugyldig konfigurasjon for relasjonen "{relation}". Den må spesifisere relasjonstype, den relaterte Active Record-klassen og en fremmednøkkel.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" prøver å velge en ugyldig kolonne "{column}". Merk at kolonnen må eksistere i en tabell eller være et uttrykk med et alias.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" er ugyldig. Sjekk at den peker på en eksisterende mappe eller fil.', + 'Application base path "{path}" is not a valid directory.' => 'Applikasjonens rot-katalog "{path}" er ikke en gyldig mappe.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Applikasjonens runtime-katalog "{path}" er ikke gyldig. Sjekk at det er en mappe som webserveren kan skrive til.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Brukeren "{user}" er allerede tildelt autorisasjonsobjektet "{item}"', + 'Base path "{path}" is not a valid directory.' => 'Rot-katalogen "{path}" er ingen gyldig mappe.', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache krever at APC er lastet.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Column name must be either a string or an array.' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => 'Feil: Tabellen "{table}" har ingen primærnøkkel.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Feil: Tabellen "{table}" har en sammensatt primærnøkkel som ikke støttes av CRUD-kommandoen.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => 'Hent ny kode', + 'Go to page: ' => 'Gå til side:', + 'Invalid MO file revision: {revision}.' => 'Ugyldig revisjon på MO-fil: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Ugyldig MO-fil: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).', + 'Last >>' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'Next >' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not exist.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The CSRF token could not be verified.' => '', + 'The STAT relation "{name}" cannot have child relations.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be published does not exist.' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The module path "{path}" is not a valid directory.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => '', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => '', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => '', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to lock file "{file}" for reading.' => 'Kan ikke lukke filen "{file}" for lesing.', + 'Unable to lock file "{file}" for writing.' => 'Kan ikke lukke filen "{file}" for skriving.', + 'Unable to read file "{file}".' => 'Kan ikke lese filen "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Kan ikke gjenta operasjonen "{object}.{method}". Funksjonen eksisterer ikke.', + 'Unable to resolve the request "{route}".' => 'Kunne ikke rute denne forespørselen "{route}".', + 'Unable to write file "{file}".' => 'Kunne ikke skrive til filen "{file}".', + 'Unknown authorization item "{name}".' => 'Ukjent autoriseringsobjekt "{name}".', + 'Unrecognized locale "{locale}".' => 'Ukjent locale "{locale}".', + 'View file "{file}" does not exist.' => 'Visningsfilen "{file}" eksisterer ikke.', + 'Yii application can only be created once.' => 'Yii-applikasjonen kan bare opprettes en gang.', + 'You are not authorized to perform this action.' => 'Du er ikke autorisert til å utføre denne operasjonen.', + 'Your request is not valid.' => 'Din forespørsel er ikke gyldig.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" er allerede tatt.', + '{attribute} "{value}" is invalid.' => '{attribute} "{value}" er ugyldig.', + '{attribute} cannot be blank.' => '{attribute} kan ikke være blank.', + '{attribute} is invalid.' => '{attribute} er ugyldig.', + '{attribute} is not a valid URL.' => '{attribute} er ikke en gyldig URL.', + '{attribute} is not a valid email address.' => '{attribute} er ikke en gyldig e-postadresse.', + '{attribute} is not in the list.' => '{attribute} er ikke i lista.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} har feil lengde (bør være {length} tegn).', + '{attribute} is too big (maximum is {max}).' => '{attribute} er for høy (maksimalt er {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} er for lang (maksimalt er {max} tegn).', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '{attribute} må være et tall.', + '{attribute} must be an integer.' => '{attribute} må være et heltallr.', + '{attribute} must be repeated exactly.' => '{attribute} må gjenntaes nøyaktig.', + '{attribute} must be {type}.' => '{attribute} må være {type}.', + '{className} does not support add() functionality.' => '{className} støtter ikke add()-funksjonalitet.', + '{className} does not support delete() functionality.' => '{className} støtter ikke delete()-funksjonalitet.', + '{className} does not support flush() functionality.' => '{className} støtter ikke flush()-funksjonalitet.', + '{className} does not support get() functionality.' => '{className} støtter ikke get()-funksjonalitet.', + '{className} does not support set() functionality.' => '{className} støtter ikke set()-funksjonalitet.', + '{class} does not have a method named "{name}".' => '{class} har ingen funksjon med navn "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} har ingen variabel "{attribute}".', + '{class} does not have attribute "{name}".' => '{class} har ingen variabel "{name}".', + '{class} does not have relation "{name}".' => '{class} har ingen relasjon "{name}".', + '{class} does not support fetching all table names.' => '{class} støtter ikke uthenting av alle tabellnavn.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} har en ugyldig valideringsregel. Regelen må spesifisere variabler som skal valideres og navnet på validatoren.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} må spesifisere "model" og verdier for "attribute" eller "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin må settes til "true" for å bruke cookie-basert autentisering.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() må implementeres.', + '{controller} cannot find the requested view "{view}".' => '{controller} kan ikke finne den forespurte siden "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} inneholder feil i markupen på siden "{view}". En {widget}-widget avsluttes ikke med et endWidget()-kall.', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} har et ekstra endWidget({id})-kall på siden.', + '{widget} cannot find the view "{view}".' => '{widget} kan ikke finne siden "{view}".', +); diff --git a/web/framework-1.0.8/messages/pl/yii.php b/web/framework-1.0.8/messages/pl/yii.php new file mode 100644 index 0000000..7a752a2 --- /dev/null +++ b/web/framework-1.0.8/messages/pl/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" nie jest poprawnym katalogiem.', + '< Previous' => '< Poprzednia', + '<< First' => '<< Pierwsza', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record wymaga komponentu aplikacji "db" CDbConnection.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Konfiguracja active record "{class}" jest niewłaściwa dla relacji" {relation}". Musi definiować typ relacji, odnośną klasę active record i klucz obcy.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" próbuje wybrać nieodpowiednią kolumnę "{column}". Pamiętaj, że kolumna musi istnieć w tabeli lub musi być wyrażona aliasem.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" jest nieprawidłowy. Upewnij się, że wskazuje na istniejący katalog lub plik.', + 'Application base path "{path}" is not a valid directory.' => 'Podstawowa ścieżka aplikacji "{path}" nie jest poprawnym katalogiem.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Ścieżka uruchomieniowa aplikacji "{path}" jest nieprawidłowa. Upewnij się proszę, że ten katalog jest dostępny do zapisu dla procesu Web serwera.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Żeton "{item}" został już przypisany użytkownikowi "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache wymaga załadowanego rozszerzenia PHP apc.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'Ścieżka CAssetManager.basePath "{path}" jest nieprawidłowa. Upewnij się proszę, że ten katalog istnieje i jest dostępny do zapisu dla procesu Web serwera.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'Identyfikator CCacheHttpSession.cacheID jest niepoprawny. Upewnij się proszę, że "{id}" odnosi się do istniejącego komponentu cacheującego aplikacji.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'Metoda CCaptchaValidator.action "{id}" jest nieprawidłowa. Nie mogę znaleźć takiej metody w bieżącym kontrolerze.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Identyfikator CDbAuthManager.connectionID "{id}" jest niewłaściwy. Upewnij się proszę, że odnosi się on do ID komponentu CDbConnection aplikacji.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Identyfikator CDbCache.connectionID "{id}" jest nieprawidłowy. Upewnij się proszę, że odnosi się on do ID komponentu CDbConnection aplikacji.', + 'CDbCacheDependency.sql cannot be empty.' => 'Wyrażenie CDbCacheDependency.sql nie może być puste.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand nie zdołał wykonać polecenia SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand nie zdołał przygotować polecenia SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection nie wspiera schematu odczytu bazy danych {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection nie zdołał otworzyć połączenia DB: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection jest nieaktywny i nie może wykonać operacji bazodanowej.', + 'CDbConnection.connectionString cannot be empty.' => 'Wyrażenie CDbConnection.connectionString nie może być puste.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader nie może przewinąć wstecz. Czyta tylko naprzód.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Identyfikator CDbHttpSession.connectionID "{id}" jest niepoprawny. Upewnij się proszę, że odnosi się on do ID komponentu CDbConnection aplikacji.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute wymaga tabeli bazodanowej "{table}" do przechowywania logu komunikatów.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'Identyfikator CDbLogRoute.connectionID "{id}" nie wskazuje na właściwy komponent CDbConnection aplikacji.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'Identyfikator CDbMessageSource.connectionID jest nieprawidłowy. Upewnij się proszę, że "{id}" odnosi się do właściwego komponentu bazodanowego aplikacji.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction jest nieaktywny i nie może wykonać poleceń COMMIT lub ROLLBACK.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'Katalog CDirectoryCacheDependency.directory nie może być pusty.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName nie może być pustym napisem.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'Ścieżka CFileLogRoute.logPath "{path}" nie wskazuje na właściwy katalog. Upewnij się proszę, że ten katalog istnieje i jest dostępny do zapisu dla procesu Web serwera.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain może przyjmować tylko obiekty implementujące interfejs IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl nie może być pusty.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name nie może być pusty.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName nie może być pusty.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'Kolekcja CHttpCookieCollection może przechowywać tylko obiekty CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest nie może określić URL skryptu wejściowego.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest nie może określić informacji o ścieżce żądania.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest nie może określić URI żądania.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode może przyjmować tylko wartości "none", "allow" lub "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'Wartość CHttpSession.gcProbability "{value}" jest nieprawidłowa. Musi być liczbą całkowitą z przedziału 0 do 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'ścieżka CHttpSession.savePath "{path}" nie jest prawidłowym katalogiem.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'Do załadowania CMemCache wymagane jest rozszerzenie PHP memcache.', + 'CMemCache server configuration must be an array.' => 'Konfiguracja serwera CMemCache musi być tablicą.', + 'CMemCache server configuration must have "host" value.' => 'Konfiguracja serwera CMemCache musi zawierać wartość "host".', + 'CMultiFileUpload.name is required.' => 'Nazwa CMultiFileUpload.name jest wymagana.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute natrafił na błędny blok kodu "{token}". Upewnij się, że wywołania Yii::beginProfile() i Yii::endProfile() są poprawnie zagnieżdżone.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'Wartość CProfileLogRoute.report "{report}" jest nieprawidłowa. Właściwe wartości zawierają "summary" i "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'Do załadowania CSecurityManager wymagane jest rozszerzenie PHP mcrypt .', + 'CSecurityManager.encryptionKey cannot be empty.' => 'Klucz CSecurityManager.encryptionKey nie może być pusty.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation musi być typu "MD5" lub "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'Klucz CSecurityManager.validationKey nie może być pusty.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'Lista CTypedList<{type}> może przechowywać tylko obiekty klasy {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat musi zawierać "path" lub "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'Do załadowania CXCache wymagane jest rozszerzenie PHP XCache.', + 'Cache table "{tableName}" does not exist.' => 'Tabela Cache "{tableName}" nie istnieje.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Nie mogę dodać "{child}" jako potomka "{name}". Wykryto zapętlenie.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Nie mogę dodać "{child}" jako potomka "{parent}". Wykryto zapętlenie.', + 'Cannot add "{name}" as a child of itself.' => 'Nie mogę dodać "{name}" jako potomka samego siebie.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Nie mogę dodać pozycji typu "{child}" do pozycji typu "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => 'Zarówno "{parent}" jak i "{child}" nie istnieją.', + 'Error: Table "{table}" does not have a primary key.' => 'Błąd: Tabela "{table}" nie ma klucza głównego.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Błąd: Tabela "{table}" zawiera klucz główny złożony, który nie jest wspierany przez polecenie crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Zdarzenie "{class}.{event}" jest połączone z niewłaściwym uchwytem "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Zdarzenie "{class}.{event}" nie jest zdefiniowane.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Nie powiódł się zapis załadowywanego pliku "{file}" na dysk.', + 'File upload was stopped by extension.' => 'Ładowanie pliku zostało zatrzymane przez rozszerzenie.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filtr "{filter}" jest niewłaściwy. Kontroler "{class}" nie zawiera metody filtra "filter{filter}".', + 'Get a new code' => 'Pobierz nowy kod', + 'Go to page: ' => 'Przejdź do strony: ', + 'Invalid MO file revision: {revision}.' => 'Nieprawidłowa korekta pliku MO: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Nieprawidłowy plik MO: {file} (liczba magiczna: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Nieprawidłowa wartość typu wyliczeniowego "{value}". Upewnij się proszę, że jest wśród wartości typu ({enum}).', + 'Last >>' => 'Ostatnia >>', + 'List data must be an array or an object implementing Traversable.' => 'Wartość listy musi być tablica lub obiektem implementującym interfejs Traversable.', + 'List index "{index}" is out of bound.' => 'Indeks listy "{index}" jest poza zakresem.', + 'Login Required' => 'Wymagane logowanie', + 'Map data must be an array or an object implementing Traversable.' => 'Dane mapowania muszą być tablicą lub obiektem implementującym interfejs Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Brak tymczasowego katalogu do zapisania wczytywanego pliku "{file}".', + 'Next >' => 'Następna>', + 'No columns are being updated for table "{table}".' => 'Żadne kolumny nie są aktualizowane w tabeli "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Żadne kolumny licznika nie są aktualizowane w tabeli "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Konfiguracja obiektu musi być tablicą zawierającą element "class".', + 'Please fix the following input errors:' => 'Popraw następujące błędy:', + 'Property "{class}.{property}" is not defined.' => 'Właściwość "{class}.{property}" nie jest zdefiniowana.', + 'Property "{class}.{property}" is read only.' => 'Właściwość "{class}.{property}" jest tylko do odczytu.', + 'Queue data must be an array or an object implementing Traversable.' => 'Dane kolejki muszą być tablicą lub muszą być obiektem implementującym interfejs Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relacja "{name}" nie jest zdefiniowana w klasie active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Dane stosu muszą być tablicą lub muszą być obiektem implementującym interfejs Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabela "{table}" nie posiada kolumny o nazwie "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Tabela "{table}" nie posiada zdefiniowanego klucza głównego.', + 'The "filter" property must be specified with a valid callback.' => 'Właściwość "filter" musi być określona wraz z poprawnym odwołaniem.', + 'The "pattern" property must be specified with a valid regular expression.' =>'Właściwość "pattern" musi być określona z właściwym wyrażeniem regularnym.', + 'The "view" property is required.' => 'Widok "view" jest wymagany.', + 'The CSRF token could not be verified.' => 'Żeton CSRF nie mógł zostać zweryfikowany.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Wzorzec URL "{pattern}" dla for route "{route}" nie jest poprawnym wyrażeniem regularnym.', + 'The active record cannot be deleted because it is new.' => 'Bieżący rekord nie może zostać usunięty ponieważ jest nowy.', + 'The active record cannot be inserted to database because it is not new.' => 'Bieżący rekord nie może zostać wstawiony ponieważ nie jest nowy.', + 'The active record cannot be updated because it is new.' => 'Bieżący rekord nie może zostać zaktualizowany ponieważ jest nowy.', + 'The asset "{asset}" to be published does not exist.' => 'Zasób "{asset}" przeznaczony do opublikowania nie istnieje.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Kolumna "{column}" nie jest kluczem obcym w tabeli "{table}".', + 'The command path "{path}" is not a valid directory.' => 'Ścieżka polecenia "{path}" nie jest właściwym katalogiem.', + 'The controller path "{path}" is not a valid directory.' => 'Ścieżka kontrolera "{path}" nie jest właściwym katalogiem.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Plik "{file}" nie może być załadowany. Dopuszczalne są tylko pliki z następującymi rozszerzeniami: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Plik "{file}" jest zbyt wielki. Rozmiar nie może przekraczać {limit} bajtów.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Plik "{file}" jest zbyt mały. Rozmiar nie może być mniejszy niż {limit} bajtów.', + 'The file "{file}" was only partially uploaded.' => 'Plik "{file}" został tylko częściowo załadowany.', + 'The first element in a filter configuration must be the filter class.' => 'Pierwszy element konfiguracji filtra musi być klasą filtra.', + 'The item "{name}" does not exist.' => 'Element"{name}" nie istnieje.', + 'The item "{parent}" already has a child "{child}".' => 'Element "{parent}" już ma potomka "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Ścieżka layout-u "{path}" nie jest prawidłowym katalogiem.', + 'The list is read only.' => 'Lista jest tylko do odczytu.', + 'The map is read only.' => 'Mapowanie jest tylko do odczytu.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Formatem kodowania godzin dla doby 12 godzinnej jest "h" lub "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Formatem kodowania godzin dla doby 24 godzinnej jest "H" lub "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Formatem kodowania znacznika AM/PM jest "a".', + 'The pattern for day in month must be "F".' =>'Formatem kodowania dnia w miesiącu jest "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Formatem kodowania dnia w roku jest "D", "DD" lub "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Formatem kodowania dnia miesiąca jest "d" lub "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Formatem kodowania dnia tygodnia jest "E", "EE", "EEE", "EEEE" lub "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Formatem kodowania ery jest "G", "GG", "GGG", "GGGG" lub "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Formatem kodowania godziny przed- lub popołudniu (AM/PM) jest "K" lub "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Formatem kodowania godziny w ciągu dnia jest "k" or "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Formatem kodowania minut jest "m" lub "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Formatem kodowania miesiąca jest "M", "MM", "MMM", lub "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Formatem kodowania sekund jest "s" lub "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Formatem kodowania strefy czasowej jest "z" lub "v".', + 'The pattern for week in month must be "W".' => 'Formatem kodowania numeru tygodnia w miesiącu jest be "W".', + 'The pattern for week in year must be "w".' => 'Formatem kodowania numeru tygodnia w roku jest be "w".', + 'The queue is empty.' => 'Kolejka jest pusta.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Relacja "{relation}" w bieżącym rekordzie klasy "{class}" nie jest zdefiniowana prawidłowo: w bazie danych nie można odnaleźć dołączanej tabeli "{joinTable}" podanej w kluczu obcym.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Relacja "{relation}" w bieżącym rekordzie klasy "{class}" jest opisana niekompletnym kluczem obcym. Klucz obcy musi składać się z odpowiednich kolumn obu łączonych tabel.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Relacja "{relation}" w bieżącym rekordzie klasy "{class}" jest opisana nieprawidłowym kluczem obcym is specified with an invalid foreign key "{key}". Klucz obcy nie wskazuje jednej z łączonych tabel.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Relacja "{relation}" w bieżącym rekordzie klasy "{class}" jest opisana nieprawidłowym kluczem obcym. Format klucza obcego musi być postaci "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Wywoływany kontroler "{controller}" nie istnieje.', + 'The requested view "{name}" is not found.' => 'Wywoływany widok "{name}" nie został odnaleziony.', + 'The stack is empty.' => 'Stos jest pusty.', + 'The system is unable to find the requested action "{action}".' => 'System nie może odnaleźć żądanej metody "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Ścieżka widoku systemowego The system view path "{path}" nie jest prawidłowym katalogiem.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Tabela "{table}" dla bieżącego rekordu klasy "{class}" nie została znaleziona w bazie danych.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Wartość klucza głównego "{key}" nie została podana w kwerendzie do tabeli"{table}".', + 'The verification code is incorrect.' => 'Kod weryfikujący jest niepoprawny.', + 'The view path "{path}" is not a valid directory.' => 'Ścieżka widoku "{path}" nie jest poprawnym katalogiem.', + 'Theme directory "{directory}" does not exist.' => 'Katalog tematów "{directory}" nie istnieje.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Ta zawartość wymaga <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Nie mogę dodać elementu o nazwie identycznej z już istniejącym elementem.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Nie mogę zmienić nazwy elementu. Nazwa "{name}" jest już używana przez inny element.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Nie mogę utworzyć pliku stanu aplikacji "{file}". Upewnij się, że katalog zawierający ten plik istnieje i jest dostępny do zapisu dla procesu Web serwera.', + 'Unable to find the decorator view "{view}".' => 'Nie mogę odnaleźć widoku dekoratora "{view}".', + 'Unable to find the list item.' => 'Nie mogę znaleźć elementu listy.', + 'Unable to lock file "{file}" for reading.' => 'Nie mogę zablokować pliku "{file}" do odczytu.', + 'Unable to lock file "{file}" for writing.' => 'Nie mogę zablokować pliku "{file}" do zapisu.', + 'Unable to read file "{file}".' => 'Nie mogę czytać pliku "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Nie mogę ponowić czynności "{object}.{method}". Ta metoda nie istnieje.', + 'Unable to write file "{file}".' => 'Nie mogę pisać do pliku "{file}".', + 'Unknown authorization item "{name}".' => 'Nieznany element autoryzacji "{name}".', + 'Unrecognized locale "{locale}".' => 'Nierozpoznana lokalizacja "{locale}".', + 'View file "{file}" does not exist.' => 'Plik widoku "{file}" nie istnieje.', + 'Yii application can only be created once.' => 'Aplikacja Yii może być utworzona tyko raz.', + 'You are not authorized to perform this action.' => 'Nie jesteś upoważniony do wykonania tej czynności.', + 'Your request is not valid.' => 'Twoje żądanie nie jest poprawne.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" został już wykorzystany.', + '{attribute} cannot be blank.' => 'Pole {attribute} nie może być puste.', + '{attribute} is invalid.' => 'Zawartość pola {attribute} jest nieprawidłowa.', + '{attribute} is not a valid URL.' => 'Zawartość pola {attribute} nie jest poprawnym adresem URL.', + '{attribute} is not a valid email address.' => 'Zawartość pola {attribute} nie jest poprawnym adresem email.', + '{attribute} is not in the list.' => '{attribute} nie występuje na tej liście.', + '{attribute} is of the wrong length (should be {length} characters).' => 'Pole {attribute} posiada niepoprawną długość (powinno posiadać {length} znaków).', + '{attribute} is too big (maximum is {max}).' => 'Wartość w polu {attribute} jest zbyt wielkia (maksimum stanowi {max}).', + '{attribute} is too long (maximum is {max} characters).' => 'Pole {attribute} jest za długie (maksymalna ilość znaków to {max}).', + '{attribute} is too short (minimum is {min} characters).' => 'Pole {attribute} jest za krótkie (minimalna ilość znaków to {min}).', + '{attribute} is too small (minimum is {min}).' => 'Wartość w polu {attribute} jest zbyt mała (minimum stanowi {min}).', + '{attribute} must be a number.' => 'Zawartość pola {attribute} musi być liczbą.', + '{attribute} must be an integer.' => 'Zawartość pola {attribute} musi być liczbą całkowitą.', + '{attribute} must be repeated exactly.' => '{attribute} musi być powtórzony dokładnie.', + '{attribute} must be {type}.' => 'Zawartość pola {attribute} musi być typu {type}.', + '{className} does not support add() functionality.' => '{className} nie wspiera funkcjonalności add().', + '{className} does not support delete() functionality.' => '{className} nie wspiera funkcjonalności delete().', + '{className} does not support flush() functionality.' => '{className} nie wspiera funkcjonalności flush().', + '{className} does not support get() functionality.' => '{className} nie wspiera funkcjonalności get().', + '{className} does not support set() functionality.' => '{className} nie wspiera funkcjonalności set().', + '{class} does not have attribute "{name}".' => '{class} nie posiada atrybutu "{attribute}".', + '{class} does not have attribute named "{name}".' => '{class} nie posiada atrybutu o nazwie "{name}".', + '{class} does not have relation "{name}".' => '{class} nie posiada relacji o nazwie "{name}".', + '{class} does not support fetching all table names.' => '{class} nie wspiera pobierania wszystkich nazw tabeli.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} ma niewłaściwą regułę walidacji. Reguła musi określać atrybuty, które mają zostać zweryfikowane i nazwę walidatora.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} musi określać wartości właściwości "model" i "attribute" lub "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin musi być ustawiony na true aby móc użyć autoryzacji opartej o cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() musi być zaimplementowany.', + '{controller} cannot find the requested view "{view}".' => '{controller} nie może znaleźć wymaganego widoku "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} zawiera niewłaściwie zagnieżdżone tagi widgetów w swoim widoku "{view}". Widget {widget} nie ma wywołania endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} ma dodatkowe wywołanie endWidget({id}) w swoim widoku.', + '{widget} cannot find the view "{view}".' => '{widget} nie może odnaleźć widoku "{view}".', +); diff --git a/web/framework-1.0.8/messages/pt/yii.php b/web/framework-1.0.8/messages/pt/yii.php new file mode 100644 index 0000000..8a363d9 --- /dev/null +++ b/web/framework-1.0.8/messages/pt/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" não é um diretório válido', + '< Previous' => '< Anterior', + '<< First' => '<< Primeiro', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record requer um componente de aplicação "db" CDbConnection', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active Record "{class}" tem uma configuração inválida no relacionamento "{relation}". Deve-se especificar o tipo de relacionamento, a classe de active record relacionada e a chave estrangeira', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'O active record "{class}" está tentando selecionar uma coluna inválida "{column}". Note, a coluna deve existir na tabela ou ser uma expressão de alias (apelido)', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" é inválido. Tenha certeza de apontar para um diretório ou arquivo existente.', + 'Application base path "{path}" is not a valid directory.' => 'O caminho "{path}" base da aplicação (Application Base) não é um diretório válido.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'O caminho "{path}" da Application runtime não é válido. Por favor, tenha certeza que é um diretório que o servidor Web possa escrever nele.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Item de autorização "{item}" já está assinado para o usuário "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache requer a extensão PHP APC para ser carregado.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" é inválido. Por favor, tanha certeza que o diretório existe e que o servidor Web possa escrever nele.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID é inválido. Por favor, tenha certeza de que "{id}" refere-se a um componente de cache da aplicação.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" é inválido. Não foi possível achar a ação (action) específicada pelo controle atual.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" é inválido. Por favor, tenha certeza que o valor refere-se ao ID de um componente CDbConnection da aplicação.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" é inválido. Por favor, tenha certeza que o valor refere-se ao ID de um componente CDbConnection da aplicação.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql não pode estar vazio.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand falhou ao executar o comando SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand falhou ao preparar o comando SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection não suporta leitura de schema para o {driver} do banco de dados.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection falhou ao abrir uma conecção com o banco de dados: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection está inativo e não pode realuzar qualquer operação do banco de dados.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString não pode estar vazio.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader não pode voltar. É um leitor com sentido único para o próximo valor.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" é inválido. Por favor, tenha certeza que o valor refere-se ao ID de um componente CDbConnection da aplicação.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute requer uma tabela "{table}" no banco de dados para guardar as mensagens de log.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" não aponta a um componente CDbConnection válido da aplicação.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID é inválido. Por favor, tenha certeza de que "{id}" refer-se a um componente válido de banco de dados.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction está inativo e não pode realziar as operações commit ou roll back.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory não pode estar vazio.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName não pode estar vazio.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" não aponta para um diretório válido. Tenha certeza de que o diretório existe e que o servidor Web possa escrever nele.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain suporta apenas objetos implementados pela interface IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl não pode estar vazio.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name não pode estar vazio.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName não pode estar vazio.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection apenas suporta objetos CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest não consegue determinar a URL do script de entrada (entry script).', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest é incapaz de determinar as informações do path da requisição.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest é incapaz de determinar a URI de requisição.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode somente pode ser "none", "allow" ou "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" é inválido. Ele deve ser um inteiro entre 0 e 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" não é um diretorio válido.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache requer a extensão PHP memcache carregada.', + 'CMemCache server configuration must be an array.' => 'A configuração do servidor (server) CMemCache deve ser um array.', + 'CMemCache server configuration must have "host" value.' => 'A configuração do servidor (server) CMemCache deve ter uma entrada "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name é obrigatório.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute encontrou uma combinação errada no bloco de código "{token}". Tente chamar Yii::beginProfile() e Yii::endProfile() para ser apropriadamente aninhado.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" é inválido. Valores válidos incluem "summary" e "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager requer a extensão PHP mcrypt para ser carregado em ordem para user propriedades de criptografia de dados.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey não pode ser vazio.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation deve ser "MD5" ou "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey não pode ser vazio.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> somente pode possuir objetos da classe {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat deve ser "path" ou "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache requer que a extensão PHP XCache seja carregada.', + 'Cache table "{tableName}" does not exist.' => 'A tabela de cache "{tableName}" não existe.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Não pode adicionar "{child}" como um filho de "{name}". Um loop foi detectado.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Não pode adicionar "{child}" como um filho de "{parent}". Um loop foi detectado.', + 'Cannot add "{name}" as a child of itself.' => 'Não pode adicionar "{name}" como filho dele mesmo.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Não pode adicionar um item do tipo "{child}" em um item do tipo "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" ou "{child}" não existe.', + 'Error: Table "{table}" does not have a primary key.' => 'Erro: Tabela "{table}" não tem chave primária.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Erro: Tabela "{table}" tem uma chave primária composta que não é suportada por comandos CRUD.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'O evento "{class}.{event}" está anexado a um handler "{handler}" inválido.', + 'Event "{class}.{event}" is not defined.' => 'O evento "{class}.{event}" não está definido.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Falha ao escrever o arquivo "{file}" no disco.', + 'File upload was stopped by extension.' => 'Upload de arquivo foi parado pela extensão.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'O filtro "{filter}" é inválido. O Controle "{class}" não possui o método de filtro "filter{filter}".', + 'Get a new code' => 'Gerar um novo código', + 'Go to page: ' => 'Ir à página: ', + 'Invalid MO file revision: {revision}.' => 'Revisão do arquivo MO inválido: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Arquivo MO inválido: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Valor enumerável "{value}" inválido. Por favor, verifique se ele está entre ({enum}).', + 'List data must be an array or an object implementing Traversable.' => 'Dados da lista devem ser um array ou objetos que implemente a interface Trasversable.', + 'List index "{index}" is out of bound.' => 'Índice "{index}" da lista está fora dos limites.', + 'Login Required' => 'Login requerido', + 'Map data must be an array or an object implementing Traversable.' => 'Dados do mapa devem ser um array ou objetos que implemente a interface Trasversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Faltando pasta temporária para armazenar o arquivo "{file}".', + 'Next >' => 'Próximo >', + 'No columns are being updated for table "{table}".' => 'Nenhuma coluna foi atualizada na tabela "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Nenhuma coluna foi atualizada na tabela "{table}".', //Revisar + 'Object configuration must be an array containing a "class" element.' => 'Configuração do objeto deve ser um array contendo um elemento da "classe".', + 'Please fix the following input errors:' => 'Por favor, corrija os seguintes erros:', + 'Property "{class}.{property}" is not defined.' => 'A propriedade "{class}.{property}" não está definida.', + 'Property "{class}.{property}" is read only.' => 'A propriedade "{class}.{property}" é somente leitura.', + 'Queue data must be an array or an object implementing Traversable.' => 'Dados da fila devem ser um array ou objetos que implemente a Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'A Relação "{name}" não foi definida na classe de active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Os dados da pilha devem ser um array ou objetos que implemente a Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'A tabela "{table}" não possui uma coluna chamada "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'A tabela "{table}" não possui uma chave primária definida.', + 'The "filter" property must be specified with a valid callback.' => 'A propriedade "filter" deve ser especificada com um callback válido.', + 'The "pattern" property must be specified with a valid regular expression.' => 'A propriedade "pattern" deve ser especificada com uma expressão regular válida.', + 'The "view" property is required.' => 'A propriedade "view" é requerida.', + 'The CSRF token could not be verified.' => 'O token CSRF não pode ser verificado.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'O padrão de URL "{pattern}" para a rota "{route}" não é uma expressão regular válida.', + 'The active record cannot be deleted because it is new.' => 'O active record não pode ser deletado porque ele é novo.', + 'The active record cannot be inserted to database because it is not new.' => 'O active record não pode ser inserido no banco de dados porque ele é novo.', + 'The active record cannot be updated because it is new.' => 'O active record não pode ser atualizado porque ele é novo.', + 'The asset "{asset}" to be pulished does not exist.' => 'O asset "{asset}" a ser publicado não existe.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'A coluna "{column}" não é uma chave extrangeira na tabela "{table}".', + 'The command path "{path}" is not a valid directory.' => 'O caminho "{path}" do "command" não é um diretório válido.', + 'The controller path "{path}" is not a valid directory.' => 'O endereço "{path}" do controle não é um diretório válido.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'O upload do arquivo "{file}" falhou. Somente são aceitos os arquivos com as extenções: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'O arquivo "{file}" é muito grande. Seu tamanho não pode exceder {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'O arquivo "{file}" é muito pequeno. Seu tamanho não pode ser menor que {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'O upload do arquivo "{file}" foi realizado parcialmente.', + 'The first element in a filter configuration must be the filter class.' => 'O primeiro elemento em uma configuração de filtro deve ser uma classe de filtro.', + 'The item "{name}" does not exist.' => 'O item "{name}" não existe.', + 'The item "{parent}" already has a child "{child}".' => 'O item "{parent}" já possui um filho "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'O endereço "{path}" do layout não é um diretório válido.', + 'The list is read only.' => 'A lista é somente leitura.', + 'The map is read only.' => 'O mapa é somente leitura.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'O padrão para o formato de 12 horas deve ser "h" ou "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'O padrão para o formato de 24 horas deve ser "H" ou "HH".', + 'The pattern for AM/PM marker must be "a".' => 'O padrão para o marcador AM/PM deve ser "a".', + 'The pattern for day in month must be "F".' => 'O padrão para o número do dia no mês deve ser "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'O padrão para o número do dia no ano deve ser "D", "DD" ou "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'O padrão para o dia do mês deve ser "d" ou "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'O padrão para o dia da semana deve ser "E", "EE", "EEE", "EEEE" ou "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'O padrão para era deve ser "G", "GG", "GGG", "GGGG" ou "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'O padrão para hora em AM/PM deve ser "K" ou "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'O padrão para hora no dia deve ser "k" ou "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'O padrão para minutos deve ser "m" ou "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'O padrão para mês deve ser "M", "MM", "MMM", ou "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'O padrão para segundos deve ser "s" ou "ss".', + 'The pattern for time zone must be "z" or "v".' => 'O padrão para o fuso horário deve ser "z" ou "v".', + 'The pattern for week in month must be "W".' => 'O padrão para o número da semana no mês deve ser "W".', + 'The pattern for week in year must be "w".' => 'O padrão para o número da semana no ano deve ser "w".', + 'The queue is empty.' => 'A fila está vazia.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'A relação "{relation}" na classe de acive record "{class}", não está especificada corretamente: a tabela de join "{joinTable}" fornecida pela chave extrangeira não pôde ser encontrada no banco de dados.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'A relação "{relation}" na classe de active record "{class}", está especificada com uma chave extrangeira incompleta. A chave extrangeira deve consistir em colunas que referenciam tabelas para realizar join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'A relação "{relation}" na classe de active record "{class}", está especificada com uma chave extrangeira "{key}" inválida. As chaves extrangeiras não apontam para uma tabela de join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'A relação "{relation}" na classe de active record "{class}" está especificada com uma chave extrangeira inválida. O formato de uma chave extrangeira deve ser "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'O controle "{controller}" solicitado não existe.', + 'The requested view "{name}" is not found.' => 'A visão "{name}" não foi encontrada.', + 'The stack is empty.' => 'A pilha está vazia.', + 'The system is unable to find the requested action "{action}".' => 'O sistema não foi capaz de encotrar a ação "{action}" solicitada.', + 'The system view path "{path}" is not a valid directory.' => 'O caminho "{path}" para a visão do sistema não é um diretório válido.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'A tabela "{table}" da classe de active record "{class}", não pôde ser encontrada no banco de dados.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'O valor para a chave primária "{key}" não foi fornecido ao consultar a tabela "{table}".', + 'The verification code is incorrect.' => 'Código de verificação incorreto.', + 'The view path "{path}" is not a valid directory.' => 'O caminho "{path}" para a visão não é um diretório válido.', + 'Theme directory "{directory}" does not exist.' => 'Diretorio de tema "{directory}" não existe.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Este conteúdo requer o plugin <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Impossível adicionar um ítem cujo nome é o mesmo de um outro ítem existente.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Impossível mudar o nome do ítem. O nome "{name}" já está sendo usado por outro ítem.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Impossível criar o arquivo "{file}" da aplicação. Certifique de que o diretório existe e que a escrite esteja habilitada pelo servidor Web.', + 'Unable to find the decorator view "{view}".' => 'Impossível achar a visão decorator "{view}".', + 'Unable to find the list item.' => 'Impossível encontrar o ítem da lista.', + 'Unable to lock file "{file}" for reading.' => 'Incapaz de bloquear arquivo "{file}" para leitura.', + 'Unable to lock file "{file}" for writing.' => 'Incapaz de bloquear arquivo "{file}" para escrita.', + 'Unable to read file "{file}".' => 'Incapaz de ler arquivo "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Impossível repetir a ação "{object}.{method}". O método não existe.', + 'Unable to write file "{file}".' => 'Impossível escrever no arquivo "{file}".', + 'Unknown authorization item "{name}".' => 'Autorização do ítem "{name}" desconhecida.', + 'Unrecognized locale "{locale}".' => 'Locale "{locale}" irreconhecível.', + 'View file "{file}" does not exist.' => 'Arquivo de visão "{file}" não existe.', + 'Yii application can only be created once.' => 'A aplicação Yii só pode ser criada uma vez.', + 'You are not authorized to perform this action.' => 'Você não está autorizado a realizar essa operação.', + 'Your request is not valid.' => 'Sua solicitação não é válida.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" já tem sido pego.', + '{attribute} cannot be blank.' => '{attribute} não pode ser vazio.', + '{attribute} is invalid.' => '{attribute} é inválido.', + '{attribute} is not a valid URL.' => '{attribute} não é uma URL válida.', + '{attribute} is not a valid email address.' => '{attribute} não é um endereço de e-mail válido.', + '{attribute} is not in the list.' => '{attribute} não está na lista.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} está com tamanho errado (deve ter {length} caracteres).', + '{attribute} is too big (maximum is {max}).' => '{attribute} é muito grande (máximo é {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} é muito longo (máximo é {max} caracteres).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} é muito curto (mínimo é {min} caracteres).', + '{attribute} is too small (minimum is {min}).' => '{attribute} é muito pequeno (mínimo é {min}).', + '{attribute} must be a number.' => '{attribute} deve ser um número.', + '{attribute} must be an integer.' => '{attribute} deve ser um inteiro.', + '{attribute} must be repeated exactly.' => '{attribute} deve ser exatamente repetido.', + '{attribute} must be {type}.' => '{attribute} deve ser {type}.', + '{className} does not support add() functionality.' => '{className} não suporta a funcionalidade add().', + '{className} does not support delete() functionality.' => '{className} não suporta a funcionalidade delete().', + '{className} does not support flush() functionality.' => '{className} não suporta a funcionalidade flush().', + '{className} does not support get() functionality.' => '{className} não suporta a funcionalidade get().', + '{className} does not support set() functionality.' => '{className} não suporta a funcionalidade set().', + '{class} does not have a method named "{name}".' => '{class} não possui um método chamado "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} não tem o atributo "{attribute}".', + '{class} does not have attribute "{name}".' => '{class} não tem o atributo "{name}".', + '{class} does not have relation "{name}".' => '{class} não tem a relação "{name}".', + '{class} does not support fetching all table names.' => '{class} não suporta a busca de todos os nomes das tabelas.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} tem uma regra de validação inválida. A regra deve especificar atributos para serem validados e o nome do validador.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} deve especificar os valores das propriedades "model" e "attribute" ou "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin deve ser atribuída como true para utilizar autenticação baseada em cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() deve ser implementada.', + '{controller} cannot find the requested view "{view}".' => '{controller} não pôde encontrar a visão "{view}" solicitada.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} contém tags widgets aninhadas incorretamente na sua visão "{view}". O widget {widget} não possui uma chamada endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} possui uma chamada endWidget({id}) extra em sua visão.', + '{widget} cannot find the view "{view}".' => '{widget} não encontrou a visão "{view}".', +); diff --git a/web/framework-1.0.8/messages/ro/yii.php b/web/framework-1.0.8/messages/ro/yii.php new file mode 100644 index 0000000..38cd94c --- /dev/null +++ b/web/framework-1.0.8/messages/ro/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" nu este un director valid.', + '< Previous' => '< Inapoi', + '<< First' => '<< Inceput', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record are nevoie de o componenta de aplicatie CDbConnection "db".', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Clasa Active record "{class}" are o configuratie nevalida pentru relatia "{relation}". Trebuie specificate tipul relatiei, clasa active record inrudita si foreign key-ul.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Clasa Active record "{class}" incearca sa selecteze o coloana nevalida "{column}". Retineti: coloana trebuie sa existe in tabela sau sa fie o expresie cu alias.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias-ul "{alias}" este nevalid. Asigurati-va ca reprezinta un fisier sau director existent.', + 'Application base path "{path}" is not a valid directory.' => 'Calea directorului de baza al aplicatiei "{path}" nu este un director valid.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Calea directorului runtime al aplicatiei "{path}" nu este valida. Asigurati-va ca serverul Web are drepturi de scriere in acest director.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Elementul de autorizare "{item}" a fost deja asignat utilizatorului "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache are nevoie ca extensia PHP apc sa fie incarcata.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" este nevalida. Asigurati-va ca directorul exista si serverul web are drepturi de scriere in acest director.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID este nevalid. Asigurati-va ca "{id}" se refera la o componenta valida de cache a aplicatiei.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" este nevalid. Action-ul acesta nu poate fi gasit in controller-ul curent.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" este nevalid. Asigurati-va ca se refera la un ID al unei componente CDbConnection a aplicatiei.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" este nevalid. Asigurati-va ca se refera la ID-ul unei componente CDbConnection a aplicatiei.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql trebuie sa contina ceva.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand a esuat executarea instructiunii SQL: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand a esuat prepararea instructiunii SQL: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection nu suporta citirea de schema pentru baza de date {driver}.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection a esuat deschiderea conexiunii DB: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection este inactiva si nu poate executa nici o operatie DB.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString trebuie sa contina ceva.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader nu poate derula. Poate face citire doar inainte (forward-only).', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" este nevalid. Asigurati-va ca se refera la ID-ul unei componente CDbConnection a aplicatiei.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute are nevoie de tabela "{table}" pentru a stoca mesajele de tip log.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" nu este o componenta valida CDbConnection a aplicatiei.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID este nevalid. Asigurati-va ca "{id}" se refera la o componenta valida de baze de date a aplicatiei.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction este inactiva si nu poate executa operatiile commit sau roll back.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory trebuie sa contina ceva.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName trebuie sa contina ceva.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" nu este un director valid. Asigurati-va ca acest director exista si ca serverul Web are drepturi de scriere.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain poate contine doar obiecte care implementeaza interfata IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl trebuie sa contina ceva.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name trebuie sa contina ceva.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName trebuie sa contina ceva.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection poate contine doar obiecte CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest nu poate determina URL-ul scriptului de intrare.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest nu poate determina informatiile despre cale din aceasta cerere.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest nu poate determina URI-ul cererii.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode poate fi doar "none", "allow" sau "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" este nevalid. Trebuie sa fie un intreg intre 0 si 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" este un director nevalid.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache are nevoie ca extensia PHP memcache sa fie incarcata.', + 'CMemCache server configuration must be an array.' => 'Configuratia serverului CMemCache trebuie sa fie un array.', + 'CMemCache server configuration must have "host" value.' => 'Configuratia serverului CMemCache trebuie sa aiba valoarea "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name este obligatoriu.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute a gasit un bloc de cod nepotrivit "{token}". Asigurati-va ca apelurile Yii::beginProfile() si Yii::endProfile() sa fie imbricate corespunzator.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" este nevalid. Valorile valide sunt "summary" si "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager arenevoie ca extensia PHP mcrypt sa fie incarcata pentru a putea folosi encriptarea de date.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey trebuie sa contina ceva.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation trebuie sa fie "MD5" sau "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey trebuie sa contina ceva.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> poate contine doar obiecte de clasa {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat trebuie sa fie "path" sau "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache are nevoie ca extensia PHP XCache sa fie incarcata.', + 'Cache table "{tableName}" does not exist.' => 'Tabela cache "{tableName}" nu exista.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Nu se poate adauga "{child}" ca copil al "{name}". A fost detectata o bucla.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Nu se poate adauga "{child}" ca copil al "{parent}". A fost detectata o bucla.', + 'Cannot add "{name}" as a child of itself.' => 'Nu se poate adauga "{name}" ca propriul sau copil.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Nu se poate adauga un element de tip "{child}" la un element de tip "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" sau "{child}" nu exista.', + 'Error: Table "{table}" does not have a primary key.' => 'Eroare: Tabela "{table}" nu are o cheie primara.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Eroare: Tabela "{table}" are o cheie primara compusa care nu este suportata de comanda crud.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Evenimentul "{class}.{event}" este atasat unui handler nevalid "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Evenimentul "{class}.{event}" nu este definit.', + 'Failed to write the uploaded file "{file}" to disk.' => 'A esuat scrierea pe disc a fisierului uploadat "{file}".', + 'File upload was stopped by extension.' => 'Uploadul fisierului a fost intrerupt de catre extensie.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filtrul "{filter}" este nevalid. Controllerul "{class}" nu are metoda filtru "filter{filter}".', + 'Get a new code' => 'Luati un cod nou', + 'Go to page: ' => 'Mergeti la pagina: ', + 'Invalid MO file revision: {revision}.' => 'Revizie nevalida a fisierului MO: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Fisier MO nevalid: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Valoare enumerabila "{value}" nevalida. Asigurati-va ca este in ({enum}).', + 'Last >>' => 'Sfarsit >>', + 'List data must be an array or an object implementing Traversable.' => 'Datele listei trebuie sa fie un array sau un obiect care implementeaza Traversable.', + 'List index "{index}" is out of bound.' => 'Indexul "{index}" al listei nu mai este valid (out of bound).', + 'Login Required' => 'Login obligatoriu', + 'Map data must be an array or an object implementing Traversable.' => 'Datele de mapare trebuie sa fie un array sau un obiect care implementeaza Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Nu se gaseste directorul temporar pentru a stoca fisierul uploadat "{file}".', + 'Next >' => 'Inainte >', + 'No columns are being updated for table "{table}".' => 'Nu este actualizata nici o coloana pentru tabela "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Nu este actualizat nici un counter de coloana pentru tabela "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Configuratia obiectului trebuie sa fie un array care contine un element "class".', + 'Please fix the following input errors:' => 'Rezolvati urmatoarele erori de intrare:', + 'Property "{class}.{property}" is not defined.' => 'Proprietatea "{class}.{property}" nu este definita.', + 'Property "{class}.{property}" is read only.' => 'Proprietatea "{class}.{property}" este read-only.', + 'Queue data must be an array or an object implementing Traversable.' => 'Datele cozii trebuie sa fie un array sau un obiect care implementeaza Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relatia "{name}" nu este definita in clasa active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Datele stivei trebuie sa fie un array sau un obiect care implementeaza Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabela "{table}" nu are o coloana "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Tabela "{table}" nu are o cheie primara definita.', + 'The "filter" property must be specified with a valid callback.' => 'Proprietatea "filter" trebuie sa fie specificata cu un callback valid.', + 'The "pattern" property must be specified with a valid regular expression.' => 'Proprietatea "pattern" trebuie sa fie specificata cu o expresie regulata valida.', + 'The "view" property is required.' => 'Proprietatea "view" este obligatorie.', + 'The CSRF token could not be verified.' => 'Token-ul CSRF nu poate fi verificat.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Pattern-ul URL "{pattern}" pentru ruta "{route}" nu este o expresie regulata valida.', + 'The active record cannot be deleted because it is new.' => 'Active record nu poate fi sters pentru ca este nou.', + 'The active record cannot be inserted to database because it is not new.' => 'Active record nu poate fi inserat in baza de date pentru ca nu este nou.', + 'The active record cannot be updated because it is new.' => 'Active record nu poate fi actualizat pentru ca este nou.', + 'The asset "{asset}" to be published does not exist.' => 'Asset-ul "{asset}" care trebuie publicat nu exista.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Coloana "{column}" nu este o cheie foreign in tabela "{table}".', + 'The command path "{path}" is not a valid directory.' => 'Calea comenzii "{path}" nu este un director valid.', + 'The controller path "{path}" is not a valid directory.' => 'Calea controllerului "{path}" nu este un director valid.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Fisierul "{file}" nu poate fi uploadat. Sunt permise doar fisierle cu aceste extensii: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Fisierul "{file}" este prea mare. Marimea fisierului nu poate depasi {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Fisierul "{file}" este prea mic. Marimea fisierului nu poate fi mai mica de {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'Fisierul "{file}" a fost uploadat partial.', + 'The first element in a filter configuration must be the filter class.' => 'Primul element din configuratia filtrului trebuie sa fie clasa filtrului.', + 'The item "{name}" does not exist.' => 'Elementul "{name}" nu exista.', + 'The item "{parent}" already has a child "{child}".' => 'Elementul "{parent}" deja are un copil "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Calea layout-ului "{path}" nu este un director valid.', + 'The list is read only.' => 'Lista este read only.', + 'The map is read only.' => 'Maparea este read only.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Patternul pentru formatul cu 12 ore trebuie sa fie "h" sau "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Patternul pentru formatul cu 24 ore trebuie sa fie "H" sau "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Patternul pentru markerul AM/PM trebuie sa fie "a".', + 'The pattern for day in month must be "F".' => 'Patternul pentru ziua din luna trebuie sa fie "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Patternul pentru ziua din an trebuie sa fie "D", "DD" sau "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Patternul pentru ziua din luna trebuie sa fie "d" sau "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Patternul pentru ziua din saptamana trebuie sa fie "E", "EE", "EEE", "EEEE" sau "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Patternul pentru era trebuie sa fie "G", "GG", "GGG", "GGGG" sau "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Patternul pentru ora in AM/PM trebuie sa fie "K" sau "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Patternul pentru ora din zi trebuie sa fie "k" sau "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Patternul pentru minute trebuie sa fie "m" sau "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Patternul pentru luna trebuie sa fie "M", "MM", "MMM" sau "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Patternul pentru secunde trebuie sa fie "s" sau "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Patternul pentru timezone trebuie sa fie "z" sau "v".', + 'The pattern for week in month must be "W".' => 'Patternul pentru saptamana din luna trebuie sa fie "W".', + 'The pattern for week in year must be "w".' => 'Patternul pentru saptamana din an trebuie sa fie "w".', + 'The queue is empty.' => 'Coada este goala.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Relatia "{relation}" din clasa active record "{class}" nu este specificata corect: tabela join "{joinTable}" precizata in cheia foreign nu poate fi gasita in baza de date.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Relatia "{relation}" din clasa active record "{class}" este specificata cu o cheie foreign incompleta. Cheia foreign key trebuie sa fie formata din coloanele care se refera la ambele tabele intre care se aplica join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Relatia "{relation}" din clasa active record "{class}" este specificata cu o cheie foreign nevalid "{key}". Cheia foreign nu precizeaza nici o tabela pentru care se aplica join.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Relatia "{relation}" din clasa active record "{class}" este specificata cu o cheie foreign nevalida. Formatul cheii foreign trebuie sa fie "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Controllerul cerut "{controller}" nu exista.', + 'The requested view "{name}" is not found.' => 'View-ul cerul "{name}" nu exista.', + 'The stack is empty.' => 'Stiva e goala.', + 'The system is unable to find the requested action "{action}".' => 'Sistemul nu a putut gasi action-ul cerut "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Calea catre view-urile sistem "{path}" nu este un director valid.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Tabela "{table}" pentru clasa active record "{class}" nu poate fi gasita in baza de date.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Valoarea cheii primare "{key}" nu este furnizata atunci cand se interogheaza tabela "{table}".', + 'The verification code is incorrect.' => 'Codul de verificare este incorect.', + 'The view path "{path}" is not a valid directory.' => 'Calea view-ului "{path}" nu este un director valid.', + 'Theme directory "{directory}" does not exist.' => 'Directorul cu teme "{directory}" nu exista.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Pentru acest continut este nevoie de <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Nu se poate adauga un element al carui nume este la fel cu numele unui element existent.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Nu se poate modifica numele elementului. Numele "{name}" este deja folosit de alt element.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Nu se poate crea fisierul "{file}" cu starea aplicatiei. Asigurati-va ca directorul care contine fisierul exista, iar serverul Web are drepturi de scriere in acest director.', + 'Unable to find the decorator view "{view}".' => 'Nu se poate gasi view-ul decorativ "{view}".', + 'Unable to lock file "{file}" for reading.' => 'Nu se poate bloca fisierul "{file}" pentru citire.', + 'Unable to lock file "{file}" for writing.' => 'Nu se poate bloca fisierul "{file}" pentru scriere.', + 'Unable to read file "{file}".' => 'Nu se poate citi fisierul "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Nu se poate repeta action-ul "{object}.{method}". Metoda nu exista.', + 'Unable to write file "{file}".' => 'Nu se poate scrie fisierul "{file}".', + 'Unknown authorization item "{name}".' => 'Element necunoscut de autorizare "{name}".', + 'Unrecognized locale "{locale}".' => 'Localizare necunoscuta "{locale}".', + 'View file "{file}" does not exist.' => 'Fisierul view "{file}" nu exista.', + 'Yii application can only be created once.' => 'Aplicatia Yii poate fi creata doar o data.', + 'You are not authorized to perform this action.' => 'Nu sunteti autorizat sa executati aceasta actiune.', + 'Your request is not valid.' => 'Cererea este nevalida.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" este deja folosit.', + '{attribute} cannot be blank.' => '{attribute} nu poate fi gol.', + '{attribute} is invalid.' => '{attribute} is nevalid.', + '{attribute} is not a valid URL.' => '{attribute} nu este un URL valid.', + '{attribute} is not a valid email address.' => '{attribute} este o adresa email nevalida.', + '{attribute} is not in the list.' => '{attribute} nu este in lista.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} este de dimensiune gresita (ar trebui sa fie de {length} caractere).', + '{attribute} is too big (maximum is {max}).' => '{attribute} este prea mare (maximul este {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} este prea lung (maximul este de {max} caractere).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} este prea mic (minimul este de {min} caractere).', + '{attribute} is too small (minimum is {min}).' => '{attribute} este prea mic (minimul este {min}).', + '{attribute} must be a number.' => '{attribute} trebuie sa fie un numar.', + '{attribute} must be an integer.' => '{attribute} trebuie sa fie un intreg.', + '{attribute} must be repeated exactly.' => '{attribute} trebuie sa fie repetat exact.', + '{attribute} must be {type}.' => '{attribute} trebuie sa fie de tip {type}.', + '{className} does not support add() functionality.' => '{className} nu suporta functionalitate add().', + '{className} does not support delete() functionality.' => '{className} nu suporta functionalitate delete().', + '{className} does not support flush() functionality.' => '{className} nu suporta functionalitate flush().', + '{className} does not support get() functionality.' => '{className} nu suporta functionalitate get().', + '{className} does not support set() functionality.' => '{className} nu suporta functionalitate set().', + '{class} does not have a method named "{name}".' => '{class} nu contine metoda cu numele "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} nu contine atributul "{attribute}".', + '{class} does not have attribute "{name}".' => '{class} nu contine atributul "{name}".', + '{class} does not have relation "{name}".' => '{class} nu contine relatia "{name}".', + '{class} does not support fetching all table names.' => '{class} nu poate extrage numele tuturor tabelelor.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} are o regula de validare gresita. Regula trebuie sa specifice atributele care trebuie validate si numele clasei validator.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} trebuie sa specifice valorile proprietatilor "model" si "attribute" sau "name".', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin trebuie sa fie setat cu true pentru a putea folosi autentificare pe baza de cookie-uri.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() trebuie implementata.', + '{controller} cannot find the requested view "{view}".' => '{controller} nu poate gasi view-ul "{view}" cerut.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} contine taguri de widget imbricate necorespunzator in view-ul sau "{view}". Un widget {widget} nu are un apel endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} are un apel endWidget({id}) in plus in view-ul sau.', + '{widget} cannot find the view "{view}".' => '{widget} nu poate gasi view-ul "{view}".', +); diff --git a/web/framework-1.0.8/messages/ru/yii.php b/web/framework-1.0.8/messages/ru/yii.php new file mode 100644 index 0000000..794a166 --- /dev/null +++ b/web/framework-1.0.8/messages/ru/yii.php @@ -0,0 +1,214 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '< Previous' => '< Предыдущая', + '<< First' => '<< Первая', + 'Go to page: ' => 'Перейти к странице: ', + 'Last >>' => 'Последняя >>', + 'Next >' => 'Следующая >', + 'The asset "{asset}" to be published does not exist.' => 'Необходимый для отображения asset "{asset}" не существует.', + 'The module path "{path}" is not a valid directory.' => 'Путь модуля "{path}" не является правильной директорией.', + 'Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.' => 'Невозможно посчитать записи с составными основными ключами. Явно укажите опции для SELECT в критерии запроса.', + 'Unable to resolve the request "{route}".' => 'Невозможно обработать запрос "{route}".', + '{class} does not have a method named "{name}".' => '{class} не содержит метода "{name}".', + '{class} does not have attribute "{attribute}".' => '{class} не содержит атрибута "{attribute}".', + '{class} does not have relation "{name}".' => '{class} не содержит отношения "{name}".', + '{class} does not support fetching all table names.' => '{class} не поддерживает выборку имён всех таблиц.', + '"{path}" is not a valid directory.' => '"{path}" не является правильной директорией.', + 'Active Record requires a "db" CDbConnection application component.' => 'ActiveRecord требует компонент приложения "db" CDbConnection.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Отношение "{relation}" класса Active record "{class}" сконфигурировано не верно. Вы дожны указать тип отношения, связанный отношением класс active record и внешний ключ.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'ActiveRecord "{class}" пытается выбрать недопустимый столбец "{column}". Примечание: столбец должен существовать в таблице или являться выражением с алиасом.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Неправильный алиас "{alias}". Убедитесь, что он указывает на существующую директорию или файл.', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache требует загруженного расширения PHP APC.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" не верен. Убедитесь, что директория существует и доступна для записи пользователю, под которым запущен веб-сервер.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl не должно быть пустым.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name не должно быть пустым.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName не должно быть пустым.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection должно содержать только объекты типа CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode должно иметь значения "none", "allow" или "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache требует расширения PHP memcache.', + 'CMemCache server configuration must be an array.' => 'Конфигурация сервера CMemCache должна быть массивом.', + 'CMemCache server configuration must have "host" value.' => 'Конфигурация сервера CMemCache должна содержать значение "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name должно быть заполнено.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey не должно быть пустым.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation должно иметь значения "MD5" или "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey не должно быть пустым.', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => 'Не удалось записать загруженный файл "{file}" на диск.', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => 'Получить новый код', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => 'Необходима авторизация', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => '', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => 'Очередь пуста.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested view "{name}" is not found.' => 'Запрошенное представление "{view}" не найдено.', + 'The stack is empty.' => 'Стэк пуст.', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => 'Неправильный код проверки.', + 'The view path "{path}" is not a valid directory.' => 'Неправильный путь представления "{path}".', + 'Theme directory "{directory}" does not exist.' => 'Директория темы "{directory}" не существует.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => 'Невозможно добавить элемент с уже существующим именем.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to lock file "{file}" for reading.' => 'Невозможно заблокировать файл "{file}" для чтения.', + 'Unable to lock file "{file}" for writing.' => 'Невозможно заблокировать файл "{file}" для записи.', + 'Unable to read file "{file}".' => 'Невозможно прочитать файл "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => 'Невозможно записать в файл "{file}".', + 'Unknown authorization item "{name}".' => '', + //TODO: нет ли синонима «локаль»? + 'Unrecognized locale "{locale}".' => 'Неизвестная локаль "{locale}".', + 'View file "{file}" does not exist.' => 'Файл представления "{file}" не существует.', + 'Yii application can only be created once.' => 'Приложение Yii может быть создано только один раз.', + 'You are not authorized to perform this action.' => 'Вам неразрешено выполнять данное действие.', + 'Your request is not valid.' => 'Ваш запрос неправильный.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" уже занят.', + '{attribute} cannot be blank.' => '{attribute} не должен быть пустым.', + '{attribute} is invalid.' => '{attribute} не верен.', + '{attribute} is not a valid URL.' => '{attribute} не является правильным URL.', + '{attribute} is not a valid email address.' => '{attribute} не является правильным E-Mail адресом.', + '{attribute} is not in the list.' => '{attribute} не находится в списке.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribut} неверной длинны (Должен быть {length} симв.).', + '{attribute} is too big (maximum is {max}).' => '{attribute} слишком большой (Максимум: {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} слишком длинный (Максимум: {max} симв.).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} слишком короткий (Минимум: {min} симв.).', + '{attribute} is too small (minimum is {min}).' => '{attribute} слишком мал (Минимум: {min}).', + '{attribute} must be a number.' => '{attribute} должен быть числом.', + '{attribute} must be an integer.' => '{attribute} должен быть целым числом.', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '{attribute} должен быть {type}.', + '{className} does not support add() functionality.' => '{className} не поддерживает add() функциональность.', + '{className} does not support delete() functionality.' => '{className} не поддерживает delete() функциональность.', + '{className} does not support flush() functionality.' => '{className} не поддерживает flush() функциональность.', + '{className} does not support get() functionality.' => '{className} не поддерживает get() функциональность.', + '{className} does not support set() functionality.' => '{className} не поддерживает set() функциональность.', + '{class} does not have attribute "{name}".' => '{class} не имеет атрибут "{name}".', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => 'Определите метод {class}::authenticate().', + '{controller} cannot find the requested view "{view}".' => '{controller} не может найти представление "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '{widget} не может найти представление "{view}".', +); diff --git a/web/framework-1.0.8/messages/sr_sr/yii.php b/web/framework-1.0.8/messages/sr_sr/yii.php new file mode 100644 index 0000000..ce928f3 --- /dev/null +++ b/web/framework-1.0.8/messages/sr_sr/yii.php @@ -0,0 +1,205 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: yii.php 02:33 Thursday, February 05, 2009 unicorn $ + */ +return array ( + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest не можа да установи информације о путањи (path info) за теккући захтев (request).', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest не може да установи URI за текући захтев (zahtev).', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache захтева да екстензија PHP XCache буде учитана.', + 'Cannot add "{name}" as a child of itself.' => 'Не могу да додам "{name}" као своје дете.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Колона "{column}" није спољни кључ (foreign key) у табели "{table}".', + '"{path}" is not a valid directory.' => '"{path}" није исправан директоријум.', + 'Active Record requires a "db" CDbConnection application component.' => 'ActiveRecord захтева "db" CDbConnection апликациону компоненту.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'ActiveRecord класа "{class}" је погрешно конфигурисана за релацију "{relation}". Класа мора да специфицира тип релације, повезану (одговарајућу) active record класу и спољни кључ (foreign key).', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" класа покушава да селектује неважећу (погрешну) колону "{column}". Обратите пажњу, колона мора да постоји у табели или да буде израз са алиасом.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Алиас "{alias}" није исправан. Будите сигурни да алиас показује на постојећи директоријум или фајл.', + 'Application base path "{path}" is not a valid directory.' => 'Путања до апликације (base path) "{path}" није исправан директоријум.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Извршна путања апликације (runtime path) "{path}" није исправна. Молим вас будите сигурни да је омогућено уписивање у директоријум од стране веб сервер процеса.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Ауторизациона јединица "{item}" је већ додељена кориснику "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache захтева да PHP apc екстензија буде учитана.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" путања није исправна. Молим вас будите сигурни да је омогућено уписивање у директоријум од стране веб сервер процеса.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID је погрешан. Молим вас, будите сигурни да "{id}" референцира исправну кеш компоненту апликације.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" акција је погрешна. Не могу да пронађем такву акцију у текућем контролеру.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" је погрешан. Молим вас, будите сигурни да се референцира ID CDbConnection апликационе компоненте.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" је погрешан. Молим вас, будите сигурни да се референцира ID CDbConnection компоненте апликације.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql не може бити празан.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand није успео да изврши SQL упит: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand није успео да припреми SQL упит: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection не подржава читање шема за {driver} драјвер базе.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection није успео да отвори DB конекцију: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection није активан и не може да изврши ни једну DB операцију.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString не може бити празан.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader не може да премота (rewind). То је "само напред" (forward-only) читач.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" је погрешан. Молим вас, будите сигурни да се референцира ID CDbConnection компоненте.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute захтева табелу "{table}" у бази података за складиштење порука логовања (log messages).', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" не показује на исправну CDbConnection компоненту.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID је погрешан. Молим вас, будите сигурни да "{id}" референцира исправну database компоненту апликације.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction није активан и не може да примени commit или roll back операције.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory не може бити празан.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName не може бити празан.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" не показује на исправан директоријум. Будите сигурни да директоријум постоји и да је омогућено уписивање у директоријум од стране веб сервер процеса.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain може да прихвати једино објекте који имплементирају IFilter интерфејс.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl не може бити празан.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name не може бити празан', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName не може бити празан', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection може да садржи само CHttpCookie објекте.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest не може да установи URL улазног скрипта (entry script).', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode може бити једино "none", "allow" или "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" је погрешан. Мора бити цео број (integer) између 0 i 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" није исправан директоријум.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache захтева да PHP memcache екстензија буде учитана.', + 'CMemCache server configuration must be an array.' => 'CMemCache server конфигурација мора бити низ (array)', + 'CMemCache server configuration must have "host" value.' => 'CMemCache server konfiguracija мора имати додељену вредност за "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name је обавезно.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute је нашао неусаглашен блок кода "{token}". Будите сигурни да су позиви Yii::beginProfile() и Yii::endProfile() исправно угњеждени.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" је погрешан. Исправне вредности укључују "summary" и "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager захтева да PHP mcrypt екстензија буде укључена да би се користила могућност енкрипције података.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey не може бити празан.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation мора бити "MD5" или "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey не може бити празан', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> може да садржи једино објекте {type} класе.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat мора бити "path" или "get".', + 'Cache table "{tableName}" does not exist.' => 'Cache табела "{tableName}" не постоји.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Не могу да додам "{child}" као дете од "{name}". Детектована је петља.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Не могу да додам "{child}" као дете од "{parent}". Детектована је петља.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Не могу да додам ставку (item) типа "{child}" у ставку (item) типа "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => 'Ни "{parent}" ни "{child}" не постоје.', + 'Error: Table "{table}" does not have a primary key.' => 'Грешка: Табела "{table}" нема примарни кључ (primary key).', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Грешка: Табела "{table}" има композитни примарни кључ (composite primary key) који није подржан од стране "crud" команде.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Догађај (event) "{class}.{event}" је закачен за погрешан (неважећи) хендлер (handler) "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Догађај (event) "{class}.{event}" није дефинисан.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Неуспешно снимање подигнутог фајла (uploaded file) "{file}" на диск.', + 'File upload was stopped by extension.' => 'Подизање фајла (file upload) је заустављено од стране екстензије.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Филтер "{filter}" је погрешан. Контролер "{class}" садржи филтер метод "filter{filter}".', + 'Get a new code' => 'Добави нови код', + 'Invalid MO file revision: {revision}.' => 'Погрешна ревизија MO фајла: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Погрешан (неисправан) MO фајл: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Погрешна набројива вредност "{value}". Молим вас, будите сигурни да је из ({enum}).', + 'List data must be an array or an object implementing Traversable.' => 'Податак листе (List data) мора бити низ или објекат који имплементира Traversable.', + 'List index "{index}" is out of bound.' => 'Индекс листе "{index}" је ван опсега.', + 'Login Required' => 'Захтева се пријављивање', + 'Map data must be an array or an object implementing Traversable.' => 'Податак мапе (Map data) мора бити низ или објекат који имплементира Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Недостаје привремени фолдер за складиштење подигнутог фајла "{file}".', + 'No columns are being updated for table "{table}".' => 'Ни једна колона неће бити ажурирана у табели "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Бројач колона неће бити ажуриран за табелу "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Конфигурација објекта мора бити низ који садржи "class" елемент.', + 'Please fix the following input errors:' => 'Молим вас исправите грешке:', + 'Property "{class}.{property}" is not defined.' => 'Својство (property) "{class}.{property}" није дефинисано.', + 'Property "{class}.{property}" is read only.' => 'Својство (property) "{class}.{property}" је само за читање.', + 'Queue data must be an array or an object implementing Traversable.' => 'Податак реда (Queue data) мора бити низ или објекат који имплементира Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Релација "{name}" није дефинисана у active record класи "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Податак стека (Stack data) мора бити низ или објекат који имплементира Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Табела "{table}" не поседује колону "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Табела "{table}" нема дефинисан примарни кључ (primary key).', + 'The "filter" property must be specified with a valid callback.' => '"Филтер" свпкство(property) мора бити специфицирано повратном функцијом (callback).', + 'The "pattern" property must be specified with a valid regular expression.' => '"Pattern" својство (property) мора бити специфицирано исправним регуларним изразом (regular expression).', + 'The "view" property is required.' => '"view" својство (property) је обавезно.', + 'The CSRF token could not be verified.' => 'CSRF не може бити потврђен (верификован).', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'URL шаблон (pattern) "{pattern}" за руту "{route}" није исправан регуларни израз (regular expression).', + 'The active record cannot be deleted because it is new.' => 'Active record не може бити обрисан зато што је нов.', + 'The active record cannot be inserted to database because it is not new.' => 'Active record не може бити убачен у базу зато што је нов.', + 'The active record cannot be updated because it is new.' => 'The active record не може бити ажуриран зато што је нов.', + 'The asset "{asset}" to be pulished does not exist.' => 'Ресурс "{asset}" који треба бити објављен не постоји.', + 'The command path "{path}" is not a valid directory.' => 'Путања директоријума до команде (command path) "{path}" није исправна.', + 'The controller path "{path}" is not a valid directory.' => 'Путања директоријума до контролера "{path}" није исправна.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Фајл "{file}" не може бити подигнут (uploaded). Дозвољени су једино фајлови са екстензијама: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Фајл "{file}" је превелик. Величина фајла не сме да прекорачи {limit} бајта.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Фајл "{file}" је премали. Величина фајла не сме бити мања од {limit} бајта.', + 'The file "{file}" was only partially uploaded.' => 'Фајл "{file}" је само делимично подигнут (uploaded).', + 'The first element in a filter configuration must be the filter class.' => 'Први елемент у конфигурацији филтра мора бити класа филтра.', + 'The item "{name}" does not exist.' => 'Ставка "{name}" не постоји.', + 'The item "{parent}" already has a child "{child}".' => 'Ставка "{parent}" већ има дете "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Путања до директоријума layout "{path}" није исправна.', + 'The list is read only.' => 'Листа (List) је само за читање', + 'The map is read only.' => 'Мапа (Map) је само за читање', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Шаблон за 12 часовни формат мора бити "h" или "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Шаблон за 24 часовни формат мора бити "H" или "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Шаблон за AM/PM маркер мора бити "a".', + 'The pattern for day in month must be "F".' => 'Шаблон за дан у месецу мора бити "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Шаблон за дан у години мора бити "D", "DD" или "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Шаблон за дан месеца мора бити "d" ili "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Шаблон за дан седмице мора бити "E", "EE", "EEE", "EEEE" или "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Шаблон за доба мора бити "G", "GG", "GGG", "GGGG" или "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Шаблон за час у AM/PM мора бити "K" или "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Шаблон за час у дану мора бити "k" или "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Шаблон за минуте мора бити "m" или "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Шабон за месец мора бити "M", "MM", "MMM" или "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Шаблон за секунде мора бити "s" или "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Шаблон за зону мора бити "z" или "v".', + 'The pattern for week in month must be "W".' => 'Шаблон за седмицу у месецу мора бити "W".', + 'The pattern for week in year must be "w".' => 'Шаблон за седмицу у години мора бити "w".', + 'The queue is empty.' => 'Ред (queue) је празан.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Релација "{relation}" у active record класи "{class}" није специфицирана исправно: придружена табела (join table) "{joinTable}" задата у спољном кључу (foreign key) не може бити пронађена у бази.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Релација "{relation}" у active record класи "{class}" је специфицирана са некомплетним спољним кључем (foreign key). Спољни кључ (foreign key) мора се састојати од колона које референцирају обе табеле за спајање (joining tables).', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Релација "{relation}" u active record класи "{class}" је специфицирана погрешним спољним кључем (foreign key) "{key}". Спољни кључ (foreign key) не показује ни на једну табелу за спајање (joining tables).', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Релација "{relation}" u active record класи "{class}" је специфицирана погрешним спољним кључем (foreign key). Формат спољног кључа (foreign key) мора бити "joinTable(fk1,fk2,...)', + 'The requested controller "{controller}" does not exist.' => 'Захтевани контролер "{controller}" не постоји.', + 'The requested view "{name}" is not found.' => 'Захтевани поглед (view) "{name}" није пронађен.', + 'The stack is empty.' => 'Стек (stack) је празан.', + 'The system is unable to find the requested action "{action}".' => 'Систем не може да пронађе захтевану акцију "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Путања директоријума до системског погледа (system view) "{path}" није исправна.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Табела "{table}" за active record класу "{class}" не може бити пронађена у бази.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Вредност примарног кључа (primary key) "{key}" није прослеђена приликом извршења упита над табелом "{table}".', + 'The verification code is incorrect.' => 'Верификациони код је погрешан.', + 'The view path "{path}" is not a valid directory.' => 'Путања до директоријума view (view path) "{path}" није исправна.', + 'Theme directory "{directory}" does not exist.' => 'Директоријум за теме (theme directory) "{directory}" не постоји.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Овај садржај захтева <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Не могу да додам ставку са истим именом као постојећа ставка.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Не могу да променим име ставке (item name). Име "{name}" већ користи нека друга ставка.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Не могу да креирам апликациони фајл стања (state file) "{file}". Будите сигурни да директоријум који садржи фајл постоји и да је омогућено писање од стране веб сервер процеса.', + 'Unable to find the decorator view "{view}".' => 'Не могу да пронађем декоратерски поглед (decorator view) "{view}".', + 'Unable to find the list item.' => 'Не могу да пронађем ставку у листи (list item).', + 'Unable to lock file "{file}" for reading.' => 'Не могу да закључам фајл "{file}" за читање.', + 'Unable to lock file "{file}" for writing.' => 'Не могу да закључам фајл "{file}" за уписивање.', + 'Unable to read file "{file}".' => 'Не могу да прочитам фајл "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Не могу да репродукујем (replay) "{object}.{method}". Метод не постоји.', + 'Unable to write file "{file}".' => 'Не могу да упишем фајл "{file}".', + 'Unknown authorization item "{name}".' => 'Непозната ауторизациона ставка (authorization item) "{name}', + 'Unrecognized locale "{locale}".' => 'Локализација није препозната (unrecognized locale) "{locale}".', + 'View file "{file}" does not exist.' => 'Фајл погледа (view file) "{file}" не постоји.', + 'Yii application can only be created once.' => 'Yii апликација може бити креирана само једанпут.', + 'You are not authorized to perform this action.' => 'Нисте ауторизовани да извршите ову акцију.', + 'Your request is not valid.' => 'Ваш захтев није исправан.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" је већ узета.', + '{attribute} cannot be blank.' => 'Морате попунити поље {attribute}.', + '{attribute} is invalid.' => '{attribute} је погрешан (неисправан).', + '{attribute} is not a valid URL.' => '{attribute} није исправан URL.', + '{attribute} is not a valid email address.' => '{attribute} није исправна email адреса.', + '{attribute} is not in the list.' => '{attribute} није у листи.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} је погрешне дужине (треба бити {length} карактера).', + '{attribute} is too big (maximum is {max}).' => '{attribute} је превелик (максимум је {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} је предугачак (максимум је {max} карактера).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} је прекратак (минимум је {min} карактера).', + '{attribute} is too small (minimum is {min}).' => '{attribute} је премали (минимум је {min}).', + '{attribute} must be a number.' => '{attribute} мора бити број.', + '{attribute} must be an integer.' => '{attribute} мора бити цео број (integer).', + '{attribute} must be repeated exactly.' => '{attribute} мора бити поновљен тачно.', + '{attribute} must be {type}.' => '{attribute} мора бити {type}.', + '{className} does not support add() functionality.' => '{className} не подржава add() функционалност.', + '{className} does not support delete() functionality.' => '{className} не подржава delete() функционалност.', + '{className} does not support flush() functionality.' => '{className} не подржава flush() функционалност.', + '{className} does not support get() functionality.' => '{className} не подржава get() функционалност.', + '{className} does not support set() functionality.' => '{className} не подржава set() функционалност.', + '{class} does not have attribute "{name}".' => '{class} нем атрибут "{name}".', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} има погрешно валидационо правило (validation rule). Правило мора да специфицира атрибуте који ће бити валидирани и име валидатора.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} мора да специфицира "model" и "attribute" или "name" својство (property) вредности.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin мора бити подешен на true да би се користила cookie-based аутентификација.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() мора бити имплементирана.', + '{controller} cannot find the requested view "{view}".' => '{controller} не може да пронађе захтевани поглед (view) "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} садржи непрописно угњеждене widget тагове у свом погледу (view) "{view}". {widget} widget нема endWidget() позив.', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} има сувишан endWidget({id}) позив у свом погледу (view).', + '{widget} cannot find the view "{view}".' => '{widget} не може да пронађе поглед (view) "{view}".', +); diff --git a/web/framework-1.0.8/messages/sr_yu/yii.php b/web/framework-1.0.8/messages/sr_yu/yii.php new file mode 100644 index 0000000..f2ec891 --- /dev/null +++ b/web/framework-1.0.8/messages/sr_yu/yii.php @@ -0,0 +1,205 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: yii.php 02:34 Thursday, February 05, 2009 unicorn $ + */ +return array ( + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest ne može da ustanovi informacije o putanji (path info) za tekući zahtev (request).', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest ne može da ustanovi URI za tekući request (zahtev).', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache zahteva da ekstenzija PHP XCache bude učitana.', + 'Cannot add "{name}" as a child of itself.' => 'Ne mogu da dodam "{name}" kao svoje dete.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Kolona "{column}" nije spoljni ključ (foreign key) u tabeli "{table}".', + '"{path}" is not a valid directory.' => '"{path}" nije ispravan direktorijum.', + 'Active Record requires a "db" CDbConnection application component.' => 'ActiveRecord zahteva "db" CDbConnection aplikacionu komponentu.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'ActiveRecord-klasa "{class}" je pogrešno konfigurisana za relaciju "{relation}". Klasa mora da specificira tip relacije, povezanu (odgovarajuću) active record klasu i spoljni ključ (foreign key).', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" klasa pokušava da selektuje nevažeću (pogrešnu) kolonu "{column}". Obratite pažnju, kolona mora da postoji u tabeli ili da bude izraz sa alias-om.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Alias "{alias}" nije ispravan. Budite sigurni da alias pokazuje na postojeći direktorijum ili fajl.', + 'Application base path "{path}" is not a valid directory.' => 'Putanja do aplikacije (base path) "{path}" nije ispravan direktorijum.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Izvršna putanja aplikacije (runtime path) "{path}" nije ispravna. Molim vas budite sigurni da je omogućeno upisivanje u direktorijum od strane Web server procesa.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Autorizaciona jedinica "{item}" je već dodeljena korisniku "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache zahteva da PHP apc ekstenzija bude učitana.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" putanja nije ispravna. Molim vas budite sigurni da je omogućeno upisivanje u direktorijum od strane Web server procesa.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID je pogrešna. Molim vas, budite sigurni da "{id}" referencira ispravnu keš komponentu aplikacije.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" je pogrešan. Ne mogu da pronađem takvu akciju u tekućem kontroleru.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" je pogrešan. Molim vas, budite sigurni da se referencira ID CDbConnection aplikacione komponente komponente.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" je pogrešan. Molim vas, budite sigurni da se referencira ID CDbConnection aplikacione komponente.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql ne može biti prazan.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand nije uspeo da izvrši SQL upit: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand nije uspeo da pripremi SQL upit: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection ne podržava čitanje šema za {driver} drajver baze.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection nije uspeo da otvori DB konekciju: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection nije aktivan i ne može da izvrši ni jednu DB operaciju.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString ne može biti prazan.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader ne može da premota (rewind). To je "samo napred" (forward-only) čitač.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" je pogrešan. Molim vas, budite sigurni da se referencira ID CDbConnection komponente.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute zahteva tabelu "{table}" u bazi podataka za skladištenje poruka logovanja (log messages).', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" ne pokazuje na ispravnu CDbConnection komponentu.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID je pogrešan. Molim vas, budite sigurni da "{id}" referencira ispravnu database komponentu aplikacije.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction nije aktivan i ne može da primeni commit ili roll back operacije.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory ne može biti prazan.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName ne može biti prazan.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" ne pokazuje na ispravan direktorijum. Budite sigurni da direktorijum postoji i da je omogućeno upisivanje u direktorijum od strane Web server procesa.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain može da prihvati jedino objekte koji implementiraju IFilter interfejs.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl ne može biti prazan.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name ne može biti prazan', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName ne može biti prazan', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection može da sadrži samo CHttpCookie objekte.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest ne može da ustanovi URL ulaznog skripta (entry script).', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode može biti jedino "none", "allow" ili "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" je pogrešan. Mora biti ceo broj (integer) između 0 i 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" nije ispravan direktorijum.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache zahteva da PHP memcache ekstenzija bude učitana.', + 'CMemCache server configuration must be an array.' => 'CMemCache server konfiguracija mora biti niz (array)', + 'CMemCache server configuration must have "host" value.' => 'CMemCache server konfiguracija mora imati dodeljenu vrednost za "host".', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name je obavezno.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute je našao neusaglašen blok koda "{token}". Budite sigurni da su pozivi Yii::beginProfile() i Yii::endProfile() ispravno ugnježdeni.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" je pogrešan. Ispravne vrednosti uključuju "summary" i "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager zahteva da PHP mcrypt ekstenzija bude uključena da bi se koristila mogućnost enkripcije podataka.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey ne može biti prazan.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation mora biti "MD5" ili "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey ne može biti prazan', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> može da sadrži jedino objekte {type} klase.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat mora biti "path" ili "get".', + 'Cache table "{tableName}" does not exist.' => 'Cache tabela "{tableName}" ne postoji.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Ne mogu da dodam "{child}" kao dete od "{name}". Detektovana je petlja.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Ne mogu da dodam "{child}" kao dete od "{parent}". Detektovana je petlja.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Ne mogu da dodam stavku (item) tipa "{child}" u stavku (item) tipa "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => 'Ni "{parent}" ni "{child}" ne postoje.', + 'Error: Table "{table}" does not have a primary key.' => 'Greška: Tabela "{table}" nema primarni ključ (primary key).', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Greška: Tabela "{table}" ima kompozitni primarni ključ (composite primary key) koji nije podržan od strane "crud" komande.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Događaj "{class}.{event}" je zakačen za pogrešan (nevažeći) hendler (handler) "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Događaj (event) "{class}.{event}" nije definisan.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Neuspešno snimanje podignutog fajla (uploaded file) "{file}" na disk.', + 'File upload was stopped by extension.' => 'Podizanje fajla (file upload) je zaustavljeno od strane ekstenzije.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filter "{filter}" je pogrešan. Kontroler "{class}" sadrži filter metod "filter{filter}".', + 'Get a new code' => 'Dobavi novi kod', + 'Invalid MO file revision: {revision}.' => 'Pogrešna revizija MO fajla: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Pogrešan (neispravan) MO fajl: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Pogrešna nabrojiva vrednost "{value}". Molim vas, budite sigurni da je iz ({enum}).', + 'List data must be an array or an object implementing Traversable.' => 'Podatak liste (List data) mora biti niz ili objekat koji implementira Traversable.', + 'List index "{index}" is out of bound.' => 'Indeks liste "{index}" je van opsega.', + 'Login Required' => 'Zahteva se prijavljivanje', + 'Map data must be an array or an object implementing Traversable.' => 'Podatak mape (Map data) mora biti niz ili objekat koji implementira Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Nedostaje privremeni folder za skladištenje podignutog fajla "{file}".', + 'No columns are being updated for table "{table}".' => 'Ni jedna kolona neće biti ažurirana u tabeli "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Brojač kolona neće biti ažuriran za tebelu "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Konfiguracija objekta mora biti niz koji sadrži "class" element.', + 'Please fix the following input errors:' => 'Molim vas ispravite greške:', + 'Property "{class}.{property}" is not defined.' => 'Svojstvo (property) "{class}.{property}" nije definisano.', + 'Property "{class}.{property}" is read only.' => 'Svojstvo (property) "{class}.{property}" je samo za čitanje.', + 'Queue data must be an array or an object implementing Traversable.' => 'Podatak reda (Queue data) mora biti niz ili objekat koji implementira Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relacija "{name}" nije definisana u active record klasi "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Podatak steka (Stack data) mora biti niz ili objekat koji implementira Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabela "{table}" ne poseduje kolonu "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Tabela "{table}" nema definisan primarni ključ (primary key).', + 'The "filter" property must be specified with a valid callback.' => '"Filter" svojstvo (property) mora biti specificirano povratnom funkcijom (callback).', + 'The "pattern" property must be specified with a valid regular expression.' => '"Pattern" svojstvo (property) mora biti specificirano ispravnim regularnim izrazom (regular expression).', + 'The "view" property is required.' => '"view" svojstvo (property) je obavezno.', + 'The CSRF token could not be verified.' => 'CSRF token ne može biti potvrđen (verifikovan).', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'URL šablon (pattern) "{pattern}" za rutu "{route}" nije ispravan regularni izraz (regular expression).', + 'The active record cannot be deleted because it is new.' => 'Active record ne može biti obrisan zato što je nov.', + 'The active record cannot be inserted to database because it is not new.' => 'Active record ne može biti ubačen u bazu zato što je nov.', + 'The active record cannot be updated because it is new.' => 'The active record ne može biti ažuriran zato što je nov.', + 'The asset "{asset}" to be pulished does not exist.' => 'Resurs "{asset}" koji treba biti objavljen ne postoji.', + 'The command path "{path}" is not a valid directory.' => 'Putanja direktorijuma do komande (command path) "{path}" nije ispravna.', + 'The controller path "{path}" is not a valid directory.' => 'Putanja direktorijuma do kontrolera "{path}" nije ispravna.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Fajl "{file}" ne može biti podignut (uploaded). Dozvoljeni su jedino fajlovi sa sledećim ekstenzijama: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Fajl "{file}" je prevelik. Veličina fajla ne sme da prekorači {limit} bajta.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Fajl "{file}" je premali. Veličina fajla ne sme biti manja od {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'Fajl "{file}" je samo delimično podignut (uploaded).', + 'The first element in a filter configuration must be the filter class.' => 'Prvi element u konfiguraciji filtra mora biti klasa filtra.', + 'The item "{name}" does not exist.' => 'Stavka "{name}" ne postoji.', + 'The item "{parent}" already has a child "{child}".' => 'Stavka "{parent}" već ima dete "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Putanja do direktorijuma layout "{path}" nije ispravna.', + 'The list is read only.' => 'Lista (List) je samo za čitanje', + 'The map is read only.' => 'Mapa (Map) je samo za čitanje', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Šablon za 12 časovni format mora biti "h" ili "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Šablon za 24 časovni format mora biti "H" ili "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Šablon za AM/PM marker mora biti "a".', + 'The pattern for day in month must be "F".' => 'Šablon za dan u mesecu mora biti "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Šablon za dan u godini mora biti "D", "DD" ili "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Šablon za dan meseca mora biti "d" ili "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Šablon za dan sedmice mora biti "E", "EE", "EEE", "EEEE" ili "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Šablon za doba mora biti "G", "GG", "GGG", "GGGG" ili "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Šablon za čas u AM/PM mora biti "K" ili "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Šablon za čas u danu mora biti "k" ili "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Šablon za minute mora biti "m" ili "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Šablon za mesec mora biti "M", "MM", "MMM" ili "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Šablon za sekunde mora biti "s" ili "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Šablon za zonu mora biti "z" ili "v".', + 'The pattern for week in month must be "W".' => 'Šablon za sedmicu u mesecu mora biti "W".', + 'The pattern for week in year must be "w".' => 'Šablon za sedmicu u godini mora biti "w".', + 'The queue is empty.' => 'Red (queue) je prazan.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Relacija "{relation}" u active record klasi "{class}" nije specificirana ispravno: pridružena tabela (join table) "{joinTable}" zadata u spoljnom ključu (foreign key) ne može biti pronađena u bazi.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Relacija "{relation}" u active record klasi "{class}" je specificirana sa nekompletnim spoljnim ključem (foreign key). Spoljni ključ (foreign key) mora se sastojati od kolona koje referenciraju obe tabele za spajanje (joining tables).', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Relacija "{relation}" u active record klasi "{class}" je specificirana pogrešnim spoljnim ključem (foreign key) "{key}". Spoljni ključ (foreign key) ne pokazuje ni na jednu tabelu za spajanje (joining tables).', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Relacija "{relation}" u active record klasi "{class}" je specificirana pogrešnim spoljnim ključem (foreign key). Format spoljnog ključa (foreign key) mora biti "joinTable(fk1,fk2,...)', + 'The requested controller "{controller}" does not exist.' => 'Zahtevani kontroler "{controller}" ne postoji.', + 'The requested view "{name}" is not found.' => 'Zahtevani pogled (view) "{name}" nije pronađen.', + 'The stack is empty.' => 'Stek (stack) je prazan.', + 'The system is unable to find the requested action "{action}".' => 'Sistem ne može da pronađe zahtevanu akciju "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Putanja direktorijuma do sistemskog pogleda (system view) "{path}" nije ispravna.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Tabela "{table}" za active record klasu "{class}" ne može biti pronađena u bazi.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Vrednost primarnog ključa (primary key) "{key}" nije prosleđena prilikom izvršenja upita nad tabelom "{table}".', + 'The verification code is incorrect.' => 'Verifikacioni kod je pogrešan.', + 'The view path "{path}" is not a valid directory.' => 'Putanja do direktorijuma view (view path) "{path}" nije validna.', + 'Theme directory "{directory}" does not exist.' => 'Direktorijum za teme (theme directory) "{directory}" ne postoji.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Ovaj sadržaj zahteva <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Ne mogu da dodam stavku sa istim imenom kao postojeća stavka.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Ne mogu da promenim ime stavke (item name). Ime "{name}" već koristi neka druga stavka.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Ne mogu da kreiram aplikacioni fajl stanja (state file) "{file}". Budite sigurni da direktorijum koji sadrži fajl postoji i da je omogućeno pisanje od strane Web server procesa.', + 'Unable to find the decorator view "{view}".' => 'Ne mogu da pronađem dekoratorski pogled (decorator view) "{view}".', + 'Unable to find the list item.' => 'Ne mogu da pronađem stavku u listi (list item).', + 'Unable to lock file "{file}" for reading.' => 'Ne mogu da zaključam fajl "{file}" za čitanje.', + 'Unable to lock file "{file}" for writing.' => 'Ne mogu da zaključam fajl "{file}" za upisivanje.', + 'Unable to read file "{file}".' => 'Ne mogu da pročitam fajl "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Ne mogu da reprodukujem (replay) "{object}.{method}". Metod ne postoji.', + 'Unable to write file "{file}".' => 'Ne mogu upišem fajl "{file}".', + 'Unknown authorization item "{name}".' => 'Nepoznata autorizaciona stavka (authorization item) "{name}', + 'Unrecognized locale "{locale}".' => 'Lokalizacija nije prepoznata (unrecognized locale) "{locale}".', + 'View file "{file}" does not exist.' => 'Fajl pogleda (view file) "{file}" ne postoji.', + 'Yii application can only be created once.' => 'Yii aplikacija može biti kreirana samo jedanput.', + 'You are not authorized to perform this action.' => 'Niste autorizovani da izvršite ovu akciju.', + 'Your request is not valid.' => 'Vaš zahtev nije ispravan.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" je već uzeta.', + '{attribute} cannot be blank.' => 'Morate popuniti polje {attribute}.', + '{attribute} is invalid.' => '{attribute} je pogrešan (neispravan).', + '{attribute} is not a valid URL.' => '{attribute} nije ispravan URL.', + '{attribute} is not a valid email address.' => '{attribute} nije ispravna email adresa.', + '{attribute} is not in the list.' => '{attribute} nije u listi.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} je pogrešne dužine (treba biti {length} karaktera).', + '{attribute} is too big (maximum is {max}).' => '{attribute} je prevelik (maksimum je {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} je predugačak (maksimum je {max} karaktera).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} je prekratak (minimum je {min} karaktera).', + '{attribute} is too small (minimum is {min}).' => '{attribute} je premali (minimum je {min}).', + '{attribute} must be a number.' => '{attribute} mora biti broj.', + '{attribute} must be an integer.' => '{attribute} mora biti ceo broj (integer).', + '{attribute} must be repeated exactly.' => '{attribute} mora biti ponovljen tačno.', + '{attribute} must be {type}.' => '{attribute} mora biti {type}.', + '{className} does not support add() functionality.' => '{className} ne podržava add() funcionalnost.', + '{className} does not support delete() functionality.' => '{className} ne podržava delete() funkcionalnost.', + '{className} does not support flush() functionality.' => '{className} ne podržava flush() funkcionalnost.', + '{className} does not support get() functionality.' => '{className} ne podržava get() funkcionalnost.', + '{className} does not support set() functionality.' => '{className} ne podržava set() funkcionalnost.', + '{class} does not have attribute "{name}".' => '{class} nema atribut "{name}".', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} ima pogrešno validaciono pravilo (validation rule). Pravilo mora da specificira atribute koji ce biti validirani i ime validatora.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} mora da specificira "model" i "attribute" ili "name" svojstvo (property) vrednosti.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin mora biti podešen na true da bi se koristila cookie-based autentifikacija.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() mora biti implementirana.', + '{controller} cannot find the requested view "{view}".' => '{controller} ne može da pronađe zahtevani pogled (view) "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} sadrži nepropisno ugnježdene widget tagove u svom pogledu (view) "{view}". {widget} widget nema endWidget() poziv.', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} ima suvišan endWidget({id}) poziv u svom pogledu (view).', + '{widget} cannot find the view "{view}".' => '{widget} ne može da pronađe pogled (view) "{view}".', +); diff --git a/web/framework-1.0.8/messages/sv/yii.php b/web/framework-1.0.8/messages/sv/yii.php new file mode 100644 index 0000000..05a200c --- /dev/null +++ b/web/framework-1.0.8/messages/sv/yii.php @@ -0,0 +1,226 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + 'Column "{column} does not exist in table "{table}".' => 'Kolumnen "{column} finns inte i tabellen "{table}".', + 'Extension path "{path}" does not exist.' => 'Sökväg till utvidgning "{path}" finns inte.', + 'Invalid operator "{operator}".' => 'Ogiltig operator "{operator}".', + '{attribute} must be greater than "{compareValue}".' => '{attribute} måste vara större än "{compareValue}".', + '{attribute} must be greater than or equal to "{compareValue}".' => '{attribute} måste vara större än eller lika med "{compareValue}".', + '{attribute} must be less than "{compareValue}".' => '{attribute} måste vara mindre än "{compareValue}".', + '{attribute} must be less than or equal to "{compareValue}".' => '{attribute} måste vara mindre än eller lika med "{compareValue}".', + '{attribute} must not be equal to "{compareValue}".' => '{attribute} får inte vara lika med "{compareValue}".', + '"{path}" is not a valid directory.' => '"{path}" är inte en giltig katalog.', + '< Previous' => '< Föregående', + '<< First' => '<< Första', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record erfordrar en "db" CDbConnection applikationskomponent.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active Record-klassen "{class}" innehåller en ogiltig deklaration av relationen "{relation}". Den måste specificera relationstyp (kardinalitet), den relaterade Active Record-klassen samt referensattributet (fk).', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active Record-klassen "{class}" försöker välja en ogiltig kolumn "{column}". Observera att kolumnen måste existera i tabellen eller vara ett uttryck med alias.', + 'Active record class "{class}" does not have a scope named "{scope}".' => 'Active Record-klassen "{class}" har inte det namngivna omfånget (named scope) "{scope}".', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Ogiltigt alias "{alias}". Kontrollera att det refererar till en befintlig katalog eller fil', + 'Application base path "{path}" is not a valid directory.' => 'Applikationens rotsökväg "{path}" är inte en giltig katalog.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Applikationens runtime-sökväg "{path}" är inte giltig. Kontrollera att den är en katalog dit webbserverprocessen får skriva', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Användaren "{user}" har redan tilldelats auktoriseringsobjektet "{item}".', + 'Base path "{path}" is not a valid directory.' => 'Rotsökvägen "{path}" är inte en giltig katalog.', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache kräver att PHP apc-tillägg har laddats.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" är ogiltig. Kontrollera att katalogen existerar och att webbserverprocessen har skrivrättighet.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID är ogiltig. Kontrollera att "{id}" refererar till en giltig applikationskomponent av typen cache.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" är ogiltig. En sådan åtgärd finns inte i nuvarande "controller".', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" är ogiltig. Kontrollera att den refererar till ID för en applikationskomponent av typen CDbConnection.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" är ogiltig. Kontrollera att det refererar till ID för en CDbConnection applikationskomponent.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql får inte vara tom.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand kunde inte exekvera SQL-satsen: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand misslyckades att förbereda SQL-satsen: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection stöder inte läsning av schema för {driver}-databas.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection kunde inte öppna DB-anslutningen: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection är inaktiv och kan inte utföra någon DB-operation.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString får inte vara tom.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader kan inte reverseras. Den läser endast data i följd.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" är ogiltig. Kontrollera att det refererar till ID för en CDbConnection applikationskomponent.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" refererar inte till en giltig CDbConnection applikationskomponent.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID är ogiltig. Kontrollera att "{id}" refererar till en giltig applikationskomponent av typen database.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction är inaktiv och kan inte bekräfta eller förkasta operationer.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory får inte vara tom.', + 'CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.' => 'CEAcceleratorCache kräver att PHP-tillägget eAccelerator är laddat, aktiverat eller kompilerat med alternativet "--with-eaccelerator-shared-memory".', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName får inte vara tom.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" refererar inte till en giltig katalog. Kontrollera att den är en existerande katalog där webbserverprocessen har skrivrättighet.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain kan bara ha objekt som implementerar gränssnittet IFilter.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl kan inte vara tom.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name kan inte vara tom.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName får inte vara tom.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection kan endast innehålla objekt av typen CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest kan inte avgöra ingångsskriptets URL.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest kan inte avgöra path-informationen i förfrågningen.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest kan inte avgöra förfrågningens URI.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode kan endast vara "none", "allow" eller "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" är ogiltig. Måste vara ett heltal i intervallet 0 till 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" är inte en giltig katalog.', + 'CMemCache server configuration must be an array.' => 'CMemCache serverkonfiguration måste bestå av en array.', + 'CMemCache server configuration must have "host" value.' => 'CMemCache serverkonfiguration måste innehålla ett "host"-värde.', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name erfordras.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute upptäckte ett omatchat kodblock "{token}". Anropen till Yii::beginProfile() och Yii::endProfile() måste vara korrekt nästlade.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" är ogiltig. Giltiga värden inkluderar "summary" och "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager kräver att PHP mcrypt-tillägget om datakryptering skall användas.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey får inte vara tom.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation måste vara antingen "MD5" eller "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey får inte vara tom.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> kan endast innehålla objekt av klassen {type}.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat måste vara "path" eller "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache kräver att PHP XCache-tillägget är laddat.', + 'CZendDataCache requires PHP Zend Data Cache extension to be loaded.' => 'CZendDataCache kräver att PHP-tillägget Zend Data Cache är laddat.', + 'Cache table "{tableName}" does not exist.' => 'Cachetabell "{tableName}" finns inte.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Kan inte lägga till "{child}" som avkomma till "{name}". En slinga har detekterats.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Kan inte lägga till "{child}" som avkomma till "{parent}". En slinga har detekterats.', + 'Cannot add "{name}" as a child of itself.' => 'Kan inte lägga till "{name}" som sin egen avkomma.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Kan inte lägga ett objekt av typen "{child}" till ett objekt av typen "{parent}".', + 'Column name must be either a string or an array.' => 'Kolumnnamn måste vara antingen en sträng eller en array.', + 'Either "{parent}" or "{child}" does not exist.' => 'Antingen "{parent}" eller "{child}" existerar inte.', + 'Error: Table "{table}" does not have a primary key.' => 'Fel: Tabellen "{table}" saknar primärnyckel.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Fel: Tabellen "{table}" har en sammansatt primärnyckel vilket inte stöds av crud-kommandot.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Event "{class}.{event}" är associerad med en ogiltig hanterare "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Event "{class}.{event}" ej definierad.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Misslyckades att skriva den uppladdade filen "{file}" till disk.', + 'File upload was stopped by extension.' => 'Uppladdning av fil stoppades av tillägg.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Filtret "{filter}" är ogiltigt. Kontrollern "{class}" innehåller inte filtermetoden "filter{filter}".', + 'Get a new code' => 'Erhåll en ny kod', + 'Go to page: ' => 'Gå till sida: ', + 'Invalid MO file revision: {revision}.' => 'Ogiltig MO-filrevision: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Ogiltig MO-fil: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Ogiltigt enum-värde "{value}". Kontrollera att värdet ingår i ({enum}).', + 'Last >>' => 'Sista >>', + 'List data must be an array or an object implementing Traversable.' => 'List-innehåll måste vara en array eller ett objekt som implementerar Traversable.', + 'List index "{index}" is out of bound.' => 'Listindex "{index}" är inte inom tillåtna gränser.', + 'Login Required' => 'Inloggning krävs', + 'Map data must be an array or an object implementing Traversable.' => 'Map-innehåll måste vara en array eller ett objekt som implementerar Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Saknar temporärkatalog att placera den uppladdade filen "{file}" i.', + 'Next >' => 'Nästa >', + 'No columns are being updated for table "{table}".' => 'Inga kolumner uppdateras i tabellen "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Inga räknarkolumner uppdateras i tabellen "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Objektkonfiguration skall vara en array innehållande ett "class"-element.', + 'Please fix the following input errors:' => 'Var vänlig åtgärda följande inmatningsfel:', + 'Property "{class}.{property}" is not defined.' => 'Property "{class}.{property}" ej definierad.', + 'Property "{class}.{property}" is read only.' => 'Property "{class}.{property}" kan endast läsas.', + 'Queue data must be an array or an object implementing Traversable.' => 'Queue-innehåll måste vara en array eller ett objekt som implementerar Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Relationen "{name}" är inte definierad i Active Record-klassen "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Stack-innehåll måste vara en array eller ett objekt som implementerar Traversable.', + 'Table "{table}" does not exist.' => 'Tabellen "{table}" finns inte.', + 'Table "{table}" does not have a column named "{column}".' => 'Tabellen "{table}" innehåller inte en kolumn med namnet "{column}".', + 'The "filter" property must be specified with a valid callback.' => '"filter"-egenskapen måste specificeras med en giltig callback-metod.', + 'The "pattern" property must be specified with a valid regular expression.' => '"pattern"-egenskapen måste specificeras med ett giltigt reguljäruttryck (regexp).', + 'The CSRF token could not be verified.' => 'CSRF-symbolen (token) kunde inte verifieras.', + 'The STAT relation "{name}" cannot have child relations.' => 'STAT-sambandet "{name}" kan inte ha underordnade samband (child relations).', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'URL-mönstret "{pattern}" för vägen "{route}" är inte ett giltigt reguljäruttryck (regexp).', + 'The active record cannot be deleted because it is new.' => 'Active Record-posten kan inte tas bort eftersom den är ny.', + 'The active record cannot be inserted to database because it is not new.' => 'Active Record-posten kan inte nyskapas i databasen eftersom den inte är ny.', + 'The active record cannot be updated because it is new.' => 'Active Record-posten kan inte uppdateras eftersom den är ny.', + 'The asset "{asset}" to be published does not exist.' => 'Tillgången "{asset}" som skulle publiceras finns inte.', + 'The command path "{path}" is not a valid directory.' => 'Sökvägen till kommandot "{path}" är inte en giltig katalog.', + 'The controller path "{path}" is not a valid directory.' => 'Sökvägen "{path}" för kontrollern är inte en giltig katalog.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Filen "{file}" kan inte laddas upp. Bara filer med följande filtillägg tillåts: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Filen "{file}" är för stor. Dess storlek får inte överskrida {limit} bytes.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Filen "{file}" är för liten. Dess storlek får inte vara mindre än {limit} bytes.', + 'The file "{file}" was only partially uploaded.' => 'Filen "{file}" laddades upp ofullständigt.', + 'The first element in a filter configuration must be the filter class.' => 'Första elementet i en filterkonfiguration måste vara filtrets klass.', + 'The item "{name}" does not exist.' => 'Objektet "{name}" existerar inte.', + 'The item "{parent}" already has a child "{child}".' => 'Objektet "{parent}" har redan en avkomma "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Sökvägen "{path}" till layouten är inte en giltig katalog.', + 'The list is read only.' => 'List-innehållet är skrivskyddat.', + 'The map is read only.' => 'Map-innehållet är skrivskyddat.', + 'The module path "{path}" is not a valid directory.' => 'Modulsökvägen "{path}" är inte en giltig katalog.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Mönstret för 12-timmarsformat måste vara "h" eller "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Mönstret för 24-timmarsformat måste vara "H" eller "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Mönstret för AM/PM-märke måste vara "a".', + 'The pattern for day in month must be "F".' => 'Mönstret för dag i månad måste vara "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Mönstret för dag inom året måste vara "D", "DD" eller "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Mönstret för dag i månaden måste vara "d" eller "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Mönstret för veckodag måste vara "E", "EE", "EEE", "EEEE" eller "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Mönstret för era måste vara "G", "GG", "GGG", "GGGG" eller "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Mönstret för timme i AM/PM-format måste vara "K" eller "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Mönstret för dygnstimme måste vara "k" eller "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Mönstret för minut måste vara "m" eller "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Mönstret för månad muste vara "M", "MM", "MMM", eller "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Mönstret för sekund måste vara "s" eller "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Mönstret för tidzon måste vara "z" or "v".', + 'The pattern for week in month must be "W".' => 'Mönstret för vecka inom månaden måste vara "W".', + 'The pattern for week in year must be "w".' => 'Mönstret för vecka inom året måste vara "w".', + 'The queue is empty.' => 'Denna Queue saknar innehåll.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly. The join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Sambandet "{relation}" i active record-klassen "{class}" är inte korrekt specificerat. Mellantabellen "{joinTable}" angiven som referensattribut (FK) kan inte hittas i databasen.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Relationen "{relation}" i Active Record-klassen "{class}" är inte korrekt specificerad: jointabellen "{joinTable}" som nämns i referensattributet återfinns inte i databasen.', + 'The relation "{relation}" in active record class "{class}" is specified with a foreign key "{key}" that does not point to the parent table "{table}".' => 'Sambandet "{relation}" i active record-klassen "{class}" är specificerat med ett referensattribut (FK) "{key}" som inte pekar mot modertabellen "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Relationen "{relation}" i Active Record-klassen "{class}" är specificerad med ett ofullständigt referensattribut. Referensattributet måste bestå av kolumner refererande till båda jointabellerna.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Relationen "{relation}" i Active Record-klassen "{class}" är specificerat med ett ogiltigt referensattribut "{key}". Referensattributet refererar inte till någon av jointabellerna.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".' => 'Sambandet "{relation}" i active record-klassen "{class}" är specificerat med ett ogiltigt referensattribut (FK) "{key}". Det finns ingen sådan kolumn i tabellen "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The columns in the key must match the primary keys of the table "{table}".' => 'Sambandet "{relation}" i active record-klassen "{class}" är specificerat med ett ogiltigt referensattribut (FK). Kolumnerna i attributet (FK) måste matcha primärnyckeln i tabellen "{table}".', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Relationen "{relation}" i Active Record-klassen "{class}" är specificerad med ett ogiltigt referensattribut. Giltigt format för referensattributet är "joinTable(fk1,fk2,...)".', + 'The requested view "{name}" is not found.' => 'Den begärda vyn "{name}" hittas inte.', + 'The stack is empty.' => 'Denna Stack saknar innehåll.', + 'The system is unable to find the requested action "{action}".' => 'Systemet kan inte hitta den begärda åtgärden "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Sökvägen "{path}" för systemvyn är inte en giltig katalog.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Tabellen "{table}" för Active Record-klassen "{class}" kan inte hittas i databasen.', + 'The value for the column "{column}" is not supplied when querying the table "{table}".' => 'Värdet för kolumnen "{column}" finns inte med i fråga till tabellen "{table}".', + 'The verification code is incorrect.' => 'Verifieringskoden stämmer inte.', + 'The view path "{path}" is not a valid directory.' => 'Sökvägen "{path}" för vyn är inte en giltig katalog.', + 'Theme directory "{directory}" does not exist.' => 'Temakatalogen "{directory}" existerar inte.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Detta innehåll behöver <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a> för att visas korrekt.', + 'Unable to add an item whose name is the same as an existing item.' => 'Kan inte lägga till ett objekt med samma namn som ett existerande.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Kan inte byta namn på objektet. Namnet "{name}" är redan upptaget av ett annat objekt.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Kan inte skapa fil med applikationens state "{file}". Kontrollera att den refererar till en existerande katalog där webbserverprocessen har skrivrättighet.', + 'Unable to lock file "{file}" for reading.' => 'Kan inte låsa filen "{file}" för läsning.', + 'Unable to lock file "{file}" for writing.' => 'Kan inte låsa filen "{file}" för skrivning.', + 'Unable to read file "{file}".' => 'Kan inte läsa filen "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Kan inte återuppspela åtgärden "{object}.{method}". Metoden existerar inte.', + 'Unable to resolve the request "{route}".' => 'Kan inte lösa upp denna request "{route}".', + 'Unable to write file "{file}".' => 'Kan inte skriva filen "{file}".', + 'Unknown authorization item "{name}".' => 'Okänt auktoriseringsobjekt "{name}".', + 'Unrecognized locale "{locale}".' => 'Okänd locale "{locale}".', + 'View file "{file}" does not exist.' => 'Vyfilen "{file}" existerar inte.', + 'Yii application can only be created once.' => 'Yii applikation kan bara ha en enda instans.', + 'You are not authorized to perform this action.' => 'Du har inte auktorisation för denna åtgärd.', + 'Your request is not valid.' => 'Din begäran är ogiltig.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" är redan tagen.', + '{attribute} "{value}" is invalid.' => '{attribute} "{value}" är ogiltig.', + '{attribute} cannot be blank.' => '{attribute} får inte vara blankt.', + '{attribute} is invalid.' => '{attribute} är ogiltig.', + '{attribute} is not a valid URL.' => '{attribute} är inte en giltig URL.', + '{attribute} is not a valid email address.' => '{attribute} är inte en giltig mailadress.', + '{attribute} is not in the list.' => '{attribute} saknas i listan.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} har fel längd (skulle vara {length} tecken).', + '{attribute} is too big (maximum is {max}).' => '{attribute} är för stort (största tillåtna värde är {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} är för lång (största tillåtna längd är {max} tecken).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} är för kort (minsta tillåtna längd är {min} tecken).', + '{attribute} is too small (minimum is {min}).' => '{attribute} är för litet (minsta tillåtna värde är {min}).', + '{attribute} must be a number.' => '{attribute} måste vara ett tal.', + '{attribute} must be an integer.' => '{attribute} måste vara ett heltal.', + '{attribute} must be repeated exactly.' => '{attribute} måste repeteras exakt.', + '{attribute} must be {type}.' => '{attribute} måste vara {type}.', + '{className} does not support add() functionality.' => '{className} stöder inte add()-funktionalitet.', + '{className} does not support delete() functionality.' => '{className} stöder inte delete()-funktionalitet.', + '{className} does not support flush() functionality.' => '{className} stöder inte flush()-funktionalitet.', + '{className} does not support get() functionality.' => '{className} stöder inte get()-funktionalitet.', + '{className} does not support set() functionality.' => '{className} stöder inte set()-funktionalitet.', + '{class} does not have a method named "{name}".' => '{class} har inte en metod med namnet "{name}".', + '{class} does not have relation "{name}".' => '{class} innehåller inte relationen "{name}".', + '{class} does not support fetching all table names.' => '{class} stöder inte hämtning av alla tabellnamn.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} har en ogiltig valideringsregel. Regeln måste specifiera attribut att validera samt namnet på en valideringsfunktion.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} måste specificera "model"- och "attribute"- eller "name"-egenskapsvärden.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin måste ges värdet "true" för att cookie-baserad autentisering skall kunna användas.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() måste implementeras.', + '{controller} cannot find the requested view "{view}".' => '{controller} kan inte hitta den begärda vyn "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} innehåller olämpligt nästlade "widget"-taggar i vyn "{view}". En {widget} "widget" anropar inte endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} har ett extra anrop till endWidget({id}) i sin vy.', + '{widget} cannot find the view "{view}".' => '{widget} kan inte hitta vyn "{view}".', +); diff --git a/web/framework-1.0.8/messages/uk/yii.php b/web/framework-1.0.8/messages/uk/yii.php new file mode 100644 index 0000000..226664e --- /dev/null +++ b/web/framework-1.0.8/messages/uk/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" недійсна директорія.', + '< Previous' => '< Попередня', + '<< First' => '<< Перша', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record вимагає компонент "db" CDbConnection.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Конфігурація active record "{class}" невірна для бінарних зв\'язків (реляцій) "{relation}". Мусить визначатись тип зв\'язку, зв\'язок active record класу та зовнішнього ключа.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" пробує вибрати невірний стовпець "{column}". Примітка, цей стовпець мусить існувати в таблиці або мати псевдонім.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => 'Псевдонім "{alias}" недійсний. Переконайтеся що він вказує на існуючу директорію або файл.', + 'Application base path "{path}" is not a valid directory.' => 'Базовий шлях аплікації "{path}" вказує на недійсну директорію.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => 'Шлях виконання (runtime) аплікації "{path}" недійсний. Будь ласка, переконайтеся що ця директорія доступна для запису процесом веб-сервера.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => 'Авторизація елемента "{item}" вже було призначено користувачеві користувачеві "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache вимагає завантаженого розширення PHP apc.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'Шлях CAssetManager.basePath "{path}" недійсний. Будь ласка, переконайтеся що ця директорія доступна для запису процесом веб-сервера.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'Ідентифікатор CCacheHttpSession.cacheID недійсний. Будь ласка, переконайтеся що "{id}" вказує на дійсний компонент кешу аплікації.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'Ідентифікатор CCaptchaValidator.action "{id}" недійсний. Не вдається знайти такий метод (екшн) у поточному контролері.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Ідентифікатор CDbAuthManager.connectionID "{id}" недійсний. Будь ласка, переконайтеся що він вказує на ID компонента CDbConnection аплікації.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Ідентифікатор CDbCache.connectionID "{id}" недійсний. Будь ласка, переконайтеся що він вказує на ID компонента CDbConnection аплікації.', + 'CDbCacheDependency.sql cannot be empty.' => 'Вираз CDbCacheDependency.sql не може бути порожній.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand не вдалося виконати SQL запит: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand не вдалося приготувати SQL запит: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection не підтримує схему читання для {driver} бази даних.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection не вдалося відкрити зв\'язок до бази даних: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection не активний і не може виконувати будь-які операції з базою даних.', + 'CDbConnection.connectionString cannot be empty.' => 'Вираз CDbConnection.connectionString не може бути порожній.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader не може повернутись. Він читає лише вперед.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'Ідентифікатор CDbHttpSession.connectionID "{id}" недійсний. Будь ласка, переконайтеся що він вказує на ID компонента CDbConnection аплікації.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute вимагає таблицю "{table}" бази даних для збереження лог-повідомлень.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'Ідентифікатор CDbLogRoute.connectionID "{id}" не вказує на дійсний компонент CDbConnection аплікації.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'Ідентифікатор CDbMessageSource.connectionID недійсний. Будь ласка, переконайтеся що "{id}" вказує на дійсний компонент бази даних аплікації.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction неактивний і не може виконати COMMIT або ROLLBACK операцію.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'Директорія CDirectoryCacheDependency.directory не може бути порожньою.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName не може бути порожній.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'Шлях CFileLogRoute.logPath "{path}" не вказує на дійсну директорію. Будь ласка, переконайтеся що ця директорія існує і доступна для запису процесом веб-сервера.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain може приймати лише об\'єкти реалізації IFilter інтерфейсу.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl не може бути порожній.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name не може бути порожній.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName не може бути порожній.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'Колекція CHttpCookieCollection може зберігати тільки об\'єкти CHttpCookie.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest не в змозі визначити, запис скрипту URL.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest не в змозі визначити шлях інформації запиту.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest не в змозі визначити URI запиту.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'Значення CHttpSession.cookieMode може бути тільки "none", "allow" або "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'Значення CHttpSession.gcProbability "{value}" недійсне. Воно мусить бути цілим числом у межах від 0 до 100.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'Директорія CHttpSession.savePath "{path}" недійсна.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache вимагає завантаженого розширення PHP memcache.', + 'CMemCache server configuration must be an array.' => 'Конфігурація сервера CMemCache мусить бути масивом.', + 'CMemCache server configuration must have "host" value.' => 'Конфігурація сервера CMemCache мусить мати значення "host".', + 'CMultiFileUpload.name is required.' => 'Назва CMultiFileUpload.name обов\'язкова.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute виявив невідповідний блок коду "{token}". Переконайтеся в тому що виклики до Yii::beginProfile() i Yii::endProfile() правильно вкладені.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" недійсний. Дійсне значення включає "summary" i "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager вимагає завантаженого розширення PHP mcrypt щоб використовувати функцію шифрування даних.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'Ключ CSecurityManager.encryptionKey не може бути порожній.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation мусить бути типу "MD5" або "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'Ключ CSecurityManager.validationKey не може бути порожній.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'Список CTypedList<{type}> може містити тільки об\'єкту {type} класу.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat повинен бути або "path" або "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache вимагає завантаженого розширення PHP XCache.', + 'Cache table "{tableName}" does not exist.' => 'Кеш таблиця "{tableName}" не існує.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => 'Неможливо додати "{child}" як нащадка "{name}". Виявлено петлю (зациклення).', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => 'Неможливо додати "{child}" як нащадка "{parent}". Виявлено петлю (зациклення).', + 'Cannot add "{name}" as a child of itself.' => 'Неможливо додати "{name}" як нащадка самого себе.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => 'Неможливо додати елемент типу "{child}" до елементу типу "{parent}".', + 'Either "{parent}" or "{child}" does not exist.' => 'Один з двох "{parent}" або "{child}" не існує.', + 'Error: Table "{table}" does not have a primary key.' => 'Помилка: Таблиця "{table}" не має первинного (primary) ключа.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => 'Помилка: Таблиця "{table}" має складовий первинний ключ (composite primary key), який не підтримується crud командою.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => 'Подія "{class}.{event}" приєднана до недійсного обробника "{handler}".', + 'Event "{class}.{event}" is not defined.' => 'Подія "{class}.{event}" не визначена.', + 'Failed to write the uploaded file "{file}" to disk.' => 'Помилка при записі завантаженого файлу "{file}" на диск.', + 'File upload was stopped by extension.' => 'Завантаження файлу було припинене через розширення.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => 'Фільтр "{filter}" недійсний. контролер "{class}" не має методу фільтра "filter{filter}".', + 'Get a new code' => 'Отримати новий код', + 'Go to page: ' => 'Іти на сторінку: ', + 'Invalid MO file revision: {revision}.' => 'Недійсна ревізія MO файлу: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => 'Недійсний MO файл: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => 'Недійсне значення перелічуваного (enumerable) типу "{value}". Будь ласка, переконайтеся, що вона є однією з ({enum}).', + 'Last >>' => 'Остання >>', + 'List data must be an array or an object implementing Traversable.' => 'Дані списку мусять бути масивом або об\'єктом, що реалізує інтерфейс Traversable.', + 'List index "{index}" is out of bound.' => 'Індекс списку "{index}" є за межами.', + 'Login Required' => 'Вимагається логування', + 'Map data must be an array or an object implementing Traversable.' => 'Дані мапування мусять бути масивом або об\'єктом, що реалізує інтерфейс Traversable.', + 'Missing the temporary folder to store the uploaded file "{file}".' => 'Відсутня тимчасова директорія для збереження завантаженого файлу "{file}".', + 'Next >' => 'Наступна >', + 'No columns are being updated for table "{table}".' => 'Нема поновлених стовпців для таблиці "{table}".', + 'No counter columns are being updated for table "{table}".' => 'Нема лічильника поновлених стовпців для таблиці "{table}".', + 'Object configuration must be an array containing a "class" element.' => 'Конфігурація об\'єкту мусить бути масивом, що містить елемент "class".', + 'Please fix the following input errors:' => 'Будь ласка, виправте наступні помилки вводу:', + 'Property "{class}.{property}" is not defined.' => 'Атрибут "{class}.{property}" не визначено.', + 'Property "{class}.{property}" is read only.' => 'Атрибут "{class}.{property}" є лише для читання.', + 'Queue data must be an array or an object implementing Traversable.' => 'Дані черги повинні бути масивом або об\'єктом реалізації Traversable.', + 'Relation "{name}" is not defined in active record class "{class}".' => 'Відношення "{name}" не визначене в класі active record "{class}".', + 'Stack data must be an array or an object implementing Traversable.' => 'Дані стеку повинні бути масивом або об\'єктом реалізації Traversable.', + 'Table "{table}" does not have a column named "{column}".' => 'Таблиця "{table}" не містить стовпець з іменем "{column}".', + 'Table "{table}" does not have a primary key defined.' => 'Таблиця "{table}" не містить визначеного первинного ключа.', + 'The "filter" property must be specified with a valid callback.' => 'Атрибут "filter" мусить бути вказаний з дійсним callback.', + 'The "pattern" property must be specified with a valid regular expression.' =>'Атрибут "pattern" мусить бути вказаний з дійсним регулярним виразом.', + 'The "view" property is required.' => 'Атрибут "view" є обов\'язковим.', + 'The CSRF token could not be verified.' => 'Жетон CSRF не може бути перевірений.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'Шаблон URL "{pattern}" для роута "{route}" не є дійсним регулярним виразом.', + 'The active record cannot be deleted because it is new.' => 'Поточний запис не може бути видалений тому що він новий.', + 'The active record cannot be inserted to database because it is not new.' => 'Поточний запис не може бути вставлений у базу даних тому що він не новий.', + 'The active record cannot be updated because it is new.' => 'Поточний запис не може бути поновлений тому що він новий.', + 'The asset "{asset}" to be published does not exist.' => 'Актив"{asset}" призначений до публікації не існує.', + 'The column "{column}" is not a foreign key in table "{table}".' => 'Стовпець "{column}" не є зовнішнім ключем у таблиці "{table}".', + 'The command path "{path}" is not a valid directory.' => 'Шлях команд "{path}" вказує на недійсну директорію.', + 'The controller path "{path}" is not a valid directory.' => 'Шлях контролера "{path}" вказує на недійсну директорію.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => 'Файл "{file}" не може бути завантажено. Допускаються лише файли з наступними розширеннями: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => 'Файл "{file}" дуже великий. Розмір не може перевищувати {limit} байт.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => 'Файл "{file}" дуже малий. Розмір не може бути менший ніж {limit} байт.', + 'The file "{file}" was only partially uploaded.' => 'Файл "{file}" був тільки частково завантажений.', + 'The first element in a filter configuration must be the filter class.' => 'Перший елемент у конфігурації фільтра мусить бути класом фільтру.', + 'The item "{name}" does not exist.' => 'Елемент "{name}" не існує.', + 'The item "{parent}" already has a child "{child}".' => 'Елемент "{parent}" вже має нащадка "{child}".', + 'The layout path "{path}" is not a valid directory.' => 'Шлях для layout-у "{path}" невірний.', + 'The list is read only.' => 'Список є лише для читання.', + 'The map is read only.' => 'Мапа є лише для читання.', + 'The pattern for 12 hour format must be "h" or "hh".' => 'Шаблон для 12-годинного представлення часу має бути "h" або "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => 'Шаблон для 24-годинного представлення часу має бути "H" або "HH".', + 'The pattern for AM/PM marker must be "a".' => 'Шаблон для маркера AM/PM має бути "a".', + 'The pattern for day in month must be "F".' =>'Шаблон для дня в місяці має бути "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => 'Шаблон для дня в році має бути "D", "DD" або "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => 'Шаблон для дня місяця має бути "d" або "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => 'Шаблон для дня тижня має бути "E", "EE", "EEE", "EEEE" або "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => 'Шаблон для ери має бути "G", "GG", "GGG", "GGGG" або "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => 'Шаблон для годин перед- або після обіду (AM/PM) має бути "K" або "KK".', + 'The pattern for hour in day must be "k" or "kk".' => 'Шаблон для годин дня може бути "k" or "kk".', + 'The pattern for minutes must be "m" or "mm".' => 'Шаблон для хвилин має бути "m" або "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => 'Шаблон для місяця має бути "M", "MM", "MMM", or "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => 'Шаблон для секунд має бути "s" або "ss".', + 'The pattern for time zone must be "z" or "v".' => 'Шаблон для часового поясу має бути "z" або "v".', + 'The pattern for week in month must be "W".' => 'Шаблон для числа тижня в місяці має бути be "W".', + 'The pattern for week in year must be "w".' => 'Шаблон для числа тижня в році має бути be "w".', + 'The queue is empty.' => 'Черга порожня.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'Відношення "{relation}" у поточному записі класу "{class}" не визначена правильно: у базі даних нема таблиці "{joinTable}", на яку вказує зовнішній ключ.', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'Відношення "{relation}" у поточному записі класу "{class}" описане неповним зовнішнім ключем. Зовнішній ключ повинен складатися з посилань на стовпці обох об\'єднаних таблиць.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'Відношення "{relation}" у поточному записі класу "{class}" описане невірним зовнішнім ключем "{key}". Ключ не вказує на жодну з обох пов\'язаних таблиць.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'Відношення "{relation}" у поточному записі класу "{class}" описане невірним зовнішнім ключем. Формат ключа мусить мати вигляд "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => 'Запитаний контролер "{controller}" не існує.', + 'The requested view "{name}" is not found.' => 'Запитане представлення "{name}" не знайдене.', + 'The stack is empty.' => 'Стек порожній.', + 'The system is unable to find the requested action "{action}".' => 'Система не може знайти запитаного методу "{action}".', + 'The system view path "{path}" is not a valid directory.' => 'Системний шлях представлення "{path}" недійсний.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => 'Таблиця "{table}" для поточного класу запису "{class}" не знайдена в базі даних.', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => 'Значення первинного ключа "{key}" не визначається під час запиту до таблиці "{table}".', + 'The verification code is incorrect.' => 'Код верифікації невірний.', + 'The view path "{path}" is not a valid directory.' => 'Шлях представлення "{path}" вказує на недійсну директорію.', + 'Theme directory "{directory}" does not exist.' => 'Директорія Theme "{directory}" не існує.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => 'Зміст вимагає <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => 'Неможливо додати елемент, якщо вже існує елемент з таким іменем.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => 'Неможливо змінити назву елементу. Назва "{name}" вже використовується іншим елементом.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => 'Неможливо створити файл стану аплікації "{file}". Будь ласка, переконайтеся що ця директорія містить цей файл і він доступний для запису процесом веб-сервера.', + 'Unable to find the decorator view "{view}".' => 'Неможливо знайти декоратор представлення "{view}".', + 'Unable to find the list item.' => 'Неможливо знайти елемент списку.', + 'Unable to lock file "{file}" for reading.' => 'Неможливо заблокувати файл "{file}" для читання.', + 'Unable to lock file "{file}" for writing.' => 'Неможливо заблокувати файл "{file}" для запису.', + 'Unable to read file "{file}".' => 'Неможливо читати файл "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => 'Не можливо повторити дію (action) "{object}.{method}". Метод не існує.', + 'Unable to write file "{file}".' => 'Неможливо писати до файлу "{file}".', + 'Unknown authorization item "{name}".' => 'Невідомий елемент авторизації "{name}".', + 'Unrecognized locale "{locale}".' => 'Нерозпізнана локаль "{locale}".', + 'View file "{file}" does not exist.' => 'Файл представлення "{file}" не існує.', + 'Yii application can only be created once.' => 'Аплікація Yii може бути створена лише один раз.', + 'You are not authorized to perform this action.' => 'Ви не уповноважені виконувати цю дію.', + 'Your request is not valid.' => 'Ваш запит невірний.', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" вже був взятий.', + '{attribute} cannot be blank.' => '{attribute} не може бути порожній.', + '{attribute} is invalid.' => '{attribute} невірний.', + '{attribute} is not a valid URL.' => '{attribute} невірна адреса URL.', + '{attribute} is not a valid email address.' => '{attribute} невірна адреса email.', + '{attribute} is not in the list.' => '{attribute} нема у списку.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} має неправильну довжину (має бути {length} знаків).', + '{attribute} is too big (maximum is {max}).' => '{attribute} дуже великий (максимум становить {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} дуже довгий (максимум становить {max} знаків).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} дуже короткий (мінімум становить {min} знаків).', + '{attribute} is too small (minimum is {min}).' => '{attribute} дуже малий (minimum stanowi {min}).', + '{attribute} must be a number.' => '{attribute} мусить бути числом.', + '{attribute} must be an integer.' => '{attribute} мусить бути цілим.', + '{attribute} must be repeated exactly.' => '{attribute} мусить бути повторений у точності.', + '{attribute} must be {type}.' => '{attribute} мусить бути типу {type}.', + '{className} does not support add() functionality.' => '{className} не підтримує функціональності add().', + '{className} does not support delete() functionality.' => '{className} не підтримує функціональності delete().', + '{className} does not support flush() functionality.' => '{className} не підтримує функціональності flush().', + '{className} does not support get() functionality.' => '{className} не підтримує функціональності get().', + '{className} does not support set() functionality.' => '{className} не підтримує функціональності set().', + '{class} does not have attribute "{name}".' => '{class} не має атрибуту "{attribute}".', + '{class} does not have attribute named "{name}".' => '{class} не має атрибуту з назвою "{name}".', + '{class} does not have relation "{name}".' => '{class} не має зв\'язку з назвою "{name}".', + '{class} does not support fetching all table names.' => '{class} не підтримує вибірку всіх назв таблиці.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} має невірне правило валідації. Правило має визначати атрибути, котрі мають перевірятись і назву валідатора.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} повинен точно визначати "model" і "attribute" або "name" значення атрибуту.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin мусить бути встановлений на true аби могти використовувати аутентифікацію, базовану на cookie.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() мусить бути реалізований.', + '{controller} cannot find the requested view "{view}".' => '{controller} не може знайти запитуване представлення "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} містить неправильно вкладені теги віджету в своєму представленні "{view}". Віджет {widget} не має endWidget() виклику.', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} має додатковий endWidget({id}) у своєму представленні.', + '{widget} cannot find the view "{view}".' => '{widget} не може знайти представлення "{view}".', +); diff --git a/web/framework-1.0.8/messages/vi/yii.php b/web/framework-1.0.8/messages/vi/yii.php new file mode 100644 index 0000000..f2132a6 --- /dev/null +++ b/web/framework-1.0.8/messages/vi/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '', + '< Previous' => '', + '<< First' => '', + 'Active Record requires a "db" CDbConnection application component.' => '', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => '', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => '', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '', + 'Application base path "{path}" is not a valid directory.' => '', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '', + 'CApcCache requires PHP apc extension to be loaded.' => '', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => '', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => '', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => '', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbCacheDependency.sql cannot be empty.' => '', + 'CDbCommand failed to execute the SQL statement: {error}' => '', + 'CDbCommand failed to prepare the SQL statement: {error}' => '', + 'CDbConnection does not support reading schema for {driver} database.' => '', + 'CDbConnection failed to open the DB connection: {error}' => '', + 'CDbConnection is inactive and cannot perform any DB operations.' => '', + 'CDbConnection.connectionString cannot be empty.' => '', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => '', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => '', + 'CDbLogRoute requires database table "{table}" to store log messages.' => '', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => '', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => '', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => '', + 'CDirectoryCacheDependency.directory cannot be empty.' => '', + 'CFileCacheDependency.fileName cannot be empty.' => '', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => '', + 'CFilterChain can only take objects implementing the IFilter interface.' => '', + 'CFlexWidget.baseUrl cannot be empty.' => '', + 'CFlexWidget.name cannot be empty.' => '', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => '', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => '', + 'CHttpRequest is unable to determine the entry script URL.' => '', + 'CHttpRequest is unable to determine the path info of the request.' => '', + 'CHttpRequest is unable to determine the request URI.' => '', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => '', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => '', + 'CHttpSession.savePath "{path}" is not a valid directory.' => '', + 'CMemCache requires PHP memcache extension to be loaded.' => '', + 'CMemCache server configuration must be an array.' => '', + 'CMemCache server configuration must have "host" value.' => '', + 'CMultiFileUpload.name is required.' => '', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => '', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => '', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => '', + 'CSecurityManager.encryptionKey cannot be empty.' => '', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => '', + 'CSecurityManager.validationKey cannot be empty.' => '', + 'CTypedList<{type}> can only hold objects of {type} class.' => '', + 'CUrlManager.UrlFormat must be either "path" or "get".' => '', + 'CXCache requires PHP XCache extension to be loaded.' => '', + 'Cache table "{tableName}" does not exist.' => '', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '', + 'Cannot add "{name}" as a child of itself.' => '', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '', + 'Either "{parent}" or "{child}" does not exist.' => '', + 'Error: Table "{table}" does not have a primary key.' => '', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '', + 'Event "{class}.{event}" is not defined.' => '', + 'Failed to write the uploaded file "{file}" to disk.' => '', + 'File upload was stopped by extension.' => '', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '', + 'Get a new code' => '', + 'Go to page: ' => 'Đến trang', + 'Invalid MO file revision: {revision}.' => '', + 'Invalid MO file: {file} (magic: {magic}).' => '', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '', + 'Last >>' => '', + 'List data must be an array or an object implementing Traversable.' => '', + 'List index "{index}" is out of bound.' => '', + 'Login Required' => '', + 'Map data must be an array or an object implementing Traversable.' => '', + 'Missing the temporary folder to store the uploaded file "{file}".' => '', + 'Next >' => 'Tiếp theo', + 'No columns are being updated for table "{table}".' => '', + 'No counter columns are being updated for table "{table}".' => '', + 'Object configuration must be an array containing a "class" element.' => '', + 'Please fix the following input errors:' => '', + 'Property "{class}.{property}" is not defined.' => '', + 'Property "{class}.{property}" is read only.' => '', + 'Queue data must be an array or an object implementing Traversable.' => '', + 'Relation "{name}" is not defined in active record class "{class}".' => '', + 'Stack data must be an array or an object implementing Traversable.' => '', + 'Table "{table}" does not have a column named "{column}".' => '', + 'Table "{table}" does not have a primary key defined.' => '', + 'The "filter" property must be specified with a valid callback.' => '', + 'The "pattern" property must be specified with a valid regular expression.' => '', + 'The "view" property is required.' => '', + 'The CSRF token could not be verified.' => '', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => '', + 'The active record cannot be deleted because it is new.' => '', + 'The active record cannot be inserted to database because it is not new.' => '', + 'The active record cannot be updated because it is new.' => '', + 'The asset "{asset}" to be published does not exist.' => '', + 'The column "{column}" is not a foreign key in table "{table}".' => '', + 'The command path "{path}" is not a valid directory.' => '', + 'The controller path "{path}" is not a valid directory.' => '', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '', + 'The file "{file}" was only partially uploaded.' => '', + 'The first element in a filter configuration must be the filter class.' => '', + 'The item "{name}" does not exist.' => '', + 'The item "{parent}" already has a child "{child}".' => '', + 'The layout path "{path}" is not a valid directory.' => '', + 'The list is read only.' => 'Đây là danh sách chỉ đọc', + 'The map is read only.' => '', + 'The pattern for 12 hour format must be "h" or "hh".' => '', + 'The pattern for 24 hour format must be "H" or "HH".' => '', + 'The pattern for AM/PM marker must be "a".' => '', + 'The pattern for day in month must be "F".' => '', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '', + 'The pattern for day of the month must be "d" or "dd".' => '', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '', + 'The pattern for hour in day must be "k" or "kk".' => '', + 'The pattern for minutes must be "m" or "mm".' => '', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '', + 'The pattern for seconds must be "s" or "ss".' => '', + 'The pattern for time zone must be "z" or "v".' => '', + 'The pattern for week in month must be "W".' => '', + 'The pattern for week in year must be "w".' => '', + 'The queue is empty.' => 'Hàng đợi đang trống', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => '', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => '', + 'The requested controller "{controller}" does not exist.' => '', + 'The requested view "{name}" is not found.' => '', + 'The stack is empty.' => 'Stack trống', + 'The system is unable to find the requested action "{action}".' => '', + 'The system view path "{path}" is not a valid directory.' => '', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '', + 'The verification code is incorrect.' => 'Mã xác nhạn không chính xác', + 'The view path "{path}" is not a valid directory.' => '', + 'Theme directory "{directory}" does not exist.' => 'Thư mục theme {directory} không tồn tại', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '', + 'Unable to add an item whose name is the same as an existing item.' => '', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '', + 'Unable to find the decorator view "{view}".' => '', + 'Unable to lock file "{file}" for reading.' => '', + 'Unable to lock file "{file}" for writing.' => '', + 'Unable to read file "{file}".' => 'Không đọc được file "{file}"', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '', + 'Unable to write file "{file}".' => '', + 'Unknown authorization item "{name}".' => '', + 'Unrecognized locale "{locale}".' => '', + 'View file "{file}" does not exist.' => '', + 'Yii application can only be created once.' => '', + 'You are not authorized to perform this action.' => '', + 'Your request is not valid.' => 'Yêu cầu không hợp lệ', + '{attribute} "{value}" has already been taken.' => '', + '{attribute} cannot be blank.' => '', + '{attribute} is invalid.' => '', + '{attribute} is not a valid URL.' => '', + '{attribute} is not a valid email address.' => '', + '{attribute} is not in the list.' => '', + '{attribute} is of the wrong length (should be {length} characters).' => '', + '{attribute} is too big (maximum is {max}).' => '', + '{attribute} is too long (maximum is {max} characters).' => '', + '{attribute} is too short (minimum is {min} characters).' => '', + '{attribute} is too small (minimum is {min}).' => '', + '{attribute} must be a number.' => '', + '{attribute} must be an integer.' => '', + '{attribute} must be repeated exactly.' => '', + '{attribute} must be {type}.' => '', + '{className} does not support add() functionality.' => '', + '{className} does not support delete() functionality.' => '', + '{className} does not support flush() functionality.' => '', + '{className} does not support get() functionality.' => '', + '{className} does not support set() functionality.' => '', + '{class} does not have a method named "{name}".' => '', + '{class} does not have attribute "{attribute}".' => '', + '{class} does not have attribute "{name}".' => '', + '{class} does not have relation "{name}".' => '', + '{class} does not support fetching all table names.' => '', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '', + '{class} must specify "model" and "attribute" or "name" property values.' => '', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '', + '{class}::authenticate() must be implemented.' => '', + '{controller} cannot find the requested view "{view}".' => '', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '', + '{controller} has an extra endWidget({id}) call in its view.' => '', + '{widget} cannot find the view "{view}".' => '', +); diff --git a/web/framework-1.0.8/messages/zh_cn/yii.php b/web/framework-1.0.8/messages/zh_cn/yii.php new file mode 100644 index 0000000..16da145 --- /dev/null +++ b/web/framework-1.0.8/messages/zh_cn/yii.php @@ -0,0 +1,213 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: yii.php 918 2009-04-05 21:05:42Z qiang.xue $ + */ +return array ( + '< Previous' => '< 前页', + '<< First' => '<< 首页', + 'Go to page: ' => '翻页: ', + 'Last >>' => '末页 >>', + 'Next >' => '后页 >', + 'The asset "{asset}" to be published does not exist.' => 'Asset文件 "{asset}" 不存在.', + '"{path}" is not a valid directory.' => '"{path}" 不是一个合法的目录.', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record 需要一个名为 "db" 的 CDbConnection 应用程序组件.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active record "{class}" 对于关联 "{relation}" 有一个无效的配置. 必须给定关联种类, 相关的 active record class 以及 foreign key.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" 正尝试选择一个无效的栏位 "{column}". 注意, 该栏位必须存在于 table 中或者是一个具别名的 expression.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '别名 "{alias}" 是无效的. 请确定它指向一个已存在的目录或文件.', + 'Application base path "{path}" is not a valid directory.' => '应用程序基准路径 "{path}" 是无效的目录.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '应用程序执行时的路径 "{path}" 是无效的. 请确定它是一个可被 Web server process 写入资料的目录.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '授权项目 "{item}" 已经被指派给使用者 "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache 要求 PHP apc extension 必须先被载入.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" 是无效的. 请确定它是一个可被 Web server process 写入资料的目录.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID 是无效的. 请确定 "{id}" 参照到一个有效的快取应用程序组件.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" 是无效的. 无法在目前的控制器中找到此一动作.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" 是无效的. 请确定它参照到一个 CDbConnection 应用程序组件的 ID.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" 是无效的. 请确定它参照到一个 CDbConnection 应用程序组件的 ID.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql 不能是空的.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand 无法执行 SQL 语句: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand 无法准备 SQL 语句: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection 不支持对 {driver} 数据库 schema 的读取.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection 无法开启数据库连线: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection 状态为未启用, 无法进行任何数据库动作.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString 不能是空的.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader 无法倒回, 只允许向前读取.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" 是无效的. 请确定它参照到一个 CDbConnection 应用程序组件的 ID.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute 要求数据库 table "{table}" 储存日志讯息.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" 未指向一个有效的 CDbConnection 应用程序组件.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID 是无效的. 请确定 "{id}" 参照到一个有效的数据库应用程序组件.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction 状态为未启用, 无法进行 commit 或 roll back 动作.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory 不能是空的.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName 不能是空的.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" does not point to a valid directory. 请确定目录存在并且允许 Web server process 写入.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain 只能取得有实现 IFilter 接口的对象.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl 不能是空的.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name 不能是空的.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName 不能是空的.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection 只能持有 CHttpCookie 对象.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest 不能确认入口脚本的 URL.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest 不能确认请求的路径信息.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest 不能确认请求的 URI.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode 只能是 "none", "allow" 或 "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" 是无效的. 它必须是介于 0 与 100之间的整数.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" 不是一个有效的目录.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache 要求 PHP memcache extension 必须先被载入.', + 'CMemCache server configuration must be an array.' => 'CMemCache 服务器配置必须是一个数组.', + 'CMemCache server configuration must have "host" value.' => 'CMemCache 服务器配置必须有 "host" 的值.', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name 是必要的.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute found a mismatching code block "{token}". 请确定对于 Yii::beginProfile() 与 Yii::endProfile() 的呼叫是适当地巢状套叠的.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" 是无效的. 有效值包含 "summary" 与 "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager 要求 PHP mcrypt extension 必须先被载入以便使用资料加密功能.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey 不能是空的.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation 必须是 "MD5" 或 "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey 不能是空的.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> 只能持有 {type} 类别的对象.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat 必须是 "path" 或 "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache 要求 PHP XCache extension 必须先被载入.', + 'Cache table "{tableName}" does not exist.' => 'Cache table "{tableName}" 不存在.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '无法新增 "{child}" 成为 "{name}" 的子项. 侦测到有迴圈产生.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '无法新增 "{child}" 成为 "{parent}" 的子项. 侦测到有迴圈产生.', + 'Cannot add "{name}" as a child of itself.' => '无法新增 "{name}" 成为它自己的子项.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '无法新增一个种类为 "{child}" 的项目到一个种类为 "{parent}" 的项目.', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" 或 "{child}" 不存在.', + 'Error: Table "{table}" does not have a primary key.' => '错误: Table "{table}" 没有定义主键.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '错误: Table "{table}" 有一个不被 crud 命令所支持的合成主键.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '事件 "{class}.{event}" 附加了一个无效的 handler "{handler}".', + 'Event "{class}.{event}" is not defined.' => '事件 "{class}.{event}" 未定义.', + 'Failed to write the uploaded file "{file}" to disk.' => '无法将已上传的文件 "{file}" 写入磁碟.', + 'File upload was stopped by extension.' => '文件上传被 extension 所停止.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '筛选器 "{filter}" 是无效的. 控制器 "{class}" 没有名为 "filter{filter}" 的筛选器方法.', + 'Get a new code' => '取得一组新代码', + 'Invalid MO file revision: {revision}.' => '无效的 MO 文件修订: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => '无效的 MO 文件: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '无效的 enumerable 值 "{value}". 请确定它在 ({enum}) 之中.', + 'List data must be an array or an object implementing Traversable.' => '列表资料必须是一个数组或是有实现 Traversable 的一个对象.', + 'List index "{index}" is out of bound.' => '列表索引 "{index}" 超出范围.', + 'Login Required' => '需要先登入系统', + 'Map data must be an array or an object implementing Traversable.' => '哈希表资料必须是一个数组或一个实现 Traversable 的对象.', + 'Missing the temporary folder to store the uploaded file "{file}".' => '缺乏暂存目录来储存已上传的文件 "{file}".', + 'No columns are being updated for table "{table}".' => 'table "{table}" 没有任何栏位将被更新.', + 'No counter columns are being updated for table "{table}".' => 'table "{table}" 没有任何计数器栏位将被更新.', + 'Object configuration must be an array containing a "class" element.' => '对象配置必须是内含有一个 "class" 元素的一个数组.', + 'Please fix the following input errors:' => '请更正下列输入错误:', + 'Property "{class}.{property}" is not defined.' => '属性 "{class}.{property}" 未被定义.', + 'Property "{class}.{property}" is read only.' => '属性 "{class}.{property}" 是只读的.', + 'Queue data must be an array or an object implementing Traversable.' => '队列资料必须是一个数组或一个实现 Traversable 的对象.', + 'Relation "{name}" is not defined in active record class "{class}".' => '关联 "{name}" 未被定义在 active record class "{class}" 中.', + 'Stack data must be an array or an object implementing Traversable.' => '堆栈资料必须是一个数组或一个实现 Traversable 的对象.', + 'Table "{table}" does not have a column named "{column}".' => 'Table "{table}" 没有名为 "{column}" 的栏位.', + 'Table "{table}" does not have a primary key defined.' => 'Table "{table}" 没有定义主键.', + 'The "filter" property must be specified with a valid callback.' => '属性 "filter" 必须以一个有效的 callback 指明.', + 'The "pattern" property must be specified with a valid regular expression.' => '属性 "pattern" 必须以一个有效的 regular expression 指明.', + 'The "view" property is required.' => '需要 "view" 属性', + 'The CSRF token could not be verified.' => 'CSRF token 无法被验证.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'route "{route}" 中的 URL 格式 "{pattern}" 不是有效的 regular expression.', + 'The active record cannot be deleted because it is new.' => 'active record 由于是新的, 无法被删除.', + 'The active record cannot be inserted to database because it is not new.' => 'active record 由于不是新的, 无法被新增到数据库.', + 'The active record cannot be updated because it is new.' => 'active record 由于是新的, 无法被更新.', + 'The asset "{asset}" to be pulished does not exist.' => '欲发布的 asset "{asset}" 不存在.', + 'The column "{column}" is not a foreign key in table "{table}".' => '栏位 "{column}" 并不是 table "{table}" 中的一个 foreign key.', + 'The command path "{path}" is not a valid directory.' => '命令路径 "{path}" 不是一个有效的目录.', + 'The controller path "{path}" is not a valid directory.' => '控制器路径 "{path}" 不是一个有效的目录.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '文件 "{file}" 无法被上传. 只有附档名如下的文件是被允许的: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '文件 "{file}" 太大. 文件大小不能超过 {limit} 位元组.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '文件 "{file}" 太小. 文件大小不能少于 {limit} 位元组.', + 'The file "{file}" was only partially uploaded.' => '文件 "{file}" 上传不完全.', + 'The first element in a filter configuration must be the filter class.' => '筛选器配置中的第一个元素必须是筛选器类别.', + 'The item "{name}" does not exist.' => '项目 "{name}" 不存在.', + 'The item "{parent}" already has a child "{child}".' => '项目 "{parent}" 已有子项目 "{child}".', + 'The layout path "{path}" is not a valid directory.' => '布局路径 "{path}" 不是一个有效的目录.', + 'The list is read only.' => '列表是只读的.', + 'The map is read only.' => '哈希表是只读的.', + 'The pattern for 12 hour format must be "h" or "hh".' => '代表12小时制的格式必须是 "h" 或 "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => '代表24小时制的格式必须是 "H" 或 "HH".', + 'The pattern for AM/PM marker must be "a".' => '代表 AM/PM 标记的格式必须是 "a".', + 'The pattern for day in month must be "F".' => '代表 day in month 的格式必须是 "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '代表 day in year 的格式必须是 "D", "DD" 或 "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => '代表 day of the month 的格式必须是 "d" 或 "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '代表 day of the week 的格式必须是 "E", "EE", "EEE", "EEEE" 或 "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '代表年代的格式必须是 "G", "GG", "GGG", "GGGG" 或 "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '代表时(AM/PM格式)的格式必须是 "K" 或 "KK".', + 'The pattern for hour in day must be "k" or "kk".' => '代表该天第几小时的格式必须是 "k" 或 "kk".', + 'The pattern for minutes must be "m" or "mm".' => '代表分的格式必须是 "m" 或 "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '代表月份的格式必须是 "M", "MM", "MMM", 或 "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => '代表秒的格式必须是 "s" 或 "ss".', + 'The pattern for time zone must be "z" or "v".' => '代表时区的格式必须是 "z" 或 "v".', + 'The pattern for week in month must be "W".' => '代表 week in month 的格式必须是 "W".', + 'The pattern for week in year must be "w".' => '代表 week in year 的格式必须是 "w".', + 'The queue is empty.' => '队列状态为空.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'active record class "{class}" 中的关联 "{relation}" 未被正确指明: 数据库中无法找到 foreign key 中所给的 join table "{joinTable}".', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'active record class "{class}" 中的关联 "{relation}" 有一个不完整的 foreign key. foreign key 必须是参照 joining tables 中的栏位所构成.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'active record class "{class}" 中的关联 "{relation}" 有一个无效的 foreign key "{key}". foreign key 未指到任一个 joining table.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'active record class "{class}" 中的关联 "{relation}" 有一个无效的 foreign key. foreign key 的格式必须是 "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => '请求的控制器 "{controller}" 不存在.', + 'The requested view "{name}" is not found.' => '请求的 view "{name}" 未找到.', + 'The stack is empty.' => '堆栈状态为空.', + 'The system is unable to find the requested action "{action}".' => '系统无法找到请求的 "{action}" 动作.', + 'The system view path "{path}" is not a valid directory.' => '系统 view 路径 "{path}" 不是一个有效的目录.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '数据库中无法找到 active record class "{class}" 对应的 table "{table}".', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '查询 table "{table}" 时未提供 primary key "{key}" 的值.', + 'The verification code is incorrect.' => '验证码不正确.', + 'The view path "{path}" is not a valid directory.' => 'view 路径 "{path}" 不是一个有效的目录.', + 'Theme directory "{directory}" does not exist.' => 'Theme 目录 "{directory}" 不存在.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '内容需有 <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => '无法新增与已存在项目名称相同的新项目.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '无法变更项目名称. 名称 "{name}" 已被其它项目使用.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '无法产生应用程序状态文件 "{file}". 请确认存放此文件的目录存在并且允许 Web server process 写入.', + 'Unable to find the decorator view "{view}".' => '无法找到 decorator view "{view}".', + 'Unable to find the list item.' => '无法找到列表项目.', + 'Unable to lock file "{file}" for reading.' => '无法锁定文件 "{file}" 进行读取.', + 'Unable to lock file "{file}" for writing.' => '无法锁定文件 "{file}" 进行写入.', + 'Unable to read file "{file}".' => '无法读取文件 "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '无法再次重演 "{object}.{method}" 动作. 这个方法不存在.', + 'Unable to write file "{file}".' => '无法写入文件 "{file}".', + 'Unknown authorization item "{name}".' => '未知的授权项目 "{name}".', + 'Unrecognized locale "{locale}".' => '无法辨识的地区设定 "{locale}".', + 'View file "{file}" does not exist.' => '名为 "{file}" 的 View 档不存在.', + 'Yii application can only be created once.' => 'Yii 应用程序只能被产生一次.', + 'You are not authorized to perform this action.' => '您未被授权执行这个动作', + 'Your request is not valid.' => '您的请求无效', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" 已被取用.', + '{attribute} cannot be blank.' => '{attribute} 不可为空白.', + '{attribute} is invalid.' => '{attribute} 无效.', + '{attribute} is not a valid URL.' => '{attribute} 不是有效的 URL.', + '{attribute} is not a valid email address.' => '{attribute} 不是有效的电子邮件地址.', + '{attribute} is not in the list.' => '{attribute} 不在列表之中.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} 长度错误 (应为 {length} 字符串).', + '{attribute} is too big (maximum is {max}).' => '{attribute} 数值太大 (最大值为 {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} 太长 (最大值为 {max} 字符串).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} 太短 (最小值为 {min} 字符串).', + '{attribute} is too small (minimum is {min}).' => '{attribute} 数值太小 (最小值为 {min}).', + '{attribute} must be a number.' => '{attribute} 必须为数字.', + '{attribute} must be an integer.' => '{attribute} 必须为整数.', + '{attribute} must be repeated exactly.' => '{attribute} 必须被重复.', + '{attribute} must be {type}.' => '{attribute} 必须为 {type}.', + '{className} does not support add() functionality.' => '{className} 不支持 add() 功能.', + '{className} does not support delete() functionality.' => '{className} 不支持 delete() 功能.', + '{className} does not support flush() functionality.' => '{className} 不支持 flush() 功能.', + '{className} does not support get() functionality.' => '{className} 不支持 get() 功能.', + '{className} does not support set() functionality.' => '{className} 不支持 set() 功能.', + '{class} does not have attribute "{name}".' => '{class} 中没有名为 "{name}" 的属性.', + '{class} does not have relation "{name}".' => '{class} 中没有名为 "{name}" 的关联.', + '{class} does not support fetching all table names.' => '{class} 不支持撷取所有 table 名称.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} 有一个无效的确认规则. 规则必须指明要被确认的属性以及确认器名称.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} 必须给定 "model" 与 "attribute" 或 "name" 属性值.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin 必须设为 true 才能使用 cookie-based 认证.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() 必须被实现.', + '{controller} cannot find the requested view "{view}".' => '{controller} 无法找到请求的视图 "{view}".', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} 在它的 view "{view}" 中含有未被适当巢状套叠的 widget 标签. {widget} widget 中没有呼叫 endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} 在它的视图中有一个额外的 endWidget({id}) 呼叫.', + '{widget} cannot find the view "{view}".' => '{widget} 无法找到这个视图 "{view}".', +); diff --git a/web/framework-1.0.8/messages/zh_tw/yii.php b/web/framework-1.0.8/messages/zh_tw/yii.php new file mode 100644 index 0000000..f086752 --- /dev/null +++ b/web/framework-1.0.8/messages/zh_tw/yii.php @@ -0,0 +1,207 @@ +<?php +/** + * Message translations. + * + * This file is automatically generated by 'yiic message' command. + * It contains the localizable messages extracted from source code. + * You may modify this file by translating the extracted messages. + * + * Each array element represents the translation (value) of a message (key). + * If the value is empty, the message is considered as not translated. + * Messages that no longer need translation will have their translations + * enclosed between a pair of '@@' marks. + * + * NOTE, this file must be saved in UTF-8 encoding. + * + * @version $Id: $ + */ +return array ( + '"{path}" is not a valid directory.' => '"{path}" 不是一個合法的目錄.', + 'Active Record requires a "db" CDbConnection application component.' => 'Active Record 需要一個名為 "db" 的 CDbConnection 應用程式元件.', + 'Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.' => 'Active record "{class}" 對於關聯 "{relation}" 有一個無效的組態設定. 必須給定關聯種類, 相關的 active record class 以及 foreign key.', + 'Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.' => 'Active record "{class}" 正嘗試選擇一個無效的欄位 "{column}". 注意, 該欄位必須存在於 table 中或者是一個具別名的 expression.', + 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' => '別名 "{alias}" 是無效的. 請確定它指向一個已存在的目錄或檔案.', + 'Application base path "{path}" is not a valid directory.' => '應用程式基準路徑 "{path}" 是無效的目錄.', + 'Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.' => '應用程式執行時的路徑 "{path}" 是無效的. 請確定它是一個可被 Web server process 寫入資料的目錄.', + 'Authorization item "{item}" has already been assigned to user "{user}".' => '授權項目 "{item}" 已經被指派給使用者 "{user}".', + 'CApcCache requires PHP apc extension to be loaded.' => 'CApcCache 要求 PHP apc extension 必須先被載入.', + 'CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.' => 'CAssetManager.basePath "{path}" 是無效的. 請確定它是一個可被 Web server process 寫入資料的目錄.', + 'CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.' => 'CCacheHttpSession.cacheID 是無效的. 請確定 "{id}" 參照到一個有效的快取應用程式元件.', + 'CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.' => 'CCaptchaValidator.action "{id}" 是無效的. 無法在目前的控制器中找到此一動作.', + 'CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbAuthManager.connectionID "{id}" 是無效的. 請確定它參照到一個 CDbConnection 應用程式元件的 ID.', + 'CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbCache.connectionID "{id}" 是無效的. 請確定它參照到一個 CDbConnection 應用程式元件的 ID.', + 'CDbCacheDependency.sql cannot be empty.' => 'CDbCacheDependency.sql 不能是空的.', + 'CDbCommand failed to execute the SQL statement: {error}' => 'CDbCommand 無法執行 SQL 陳述: {error}', + 'CDbCommand failed to prepare the SQL statement: {error}' => 'CDbCommand 無法準備 SQL 陳述: {error}', + 'CDbConnection does not support reading schema for {driver} database.' => 'CDbConnection 不支援對 {driver} 資料庫 schema 的讀取.', + 'CDbConnection failed to open the DB connection: {error}' => 'CDbConnection 無法開啟資料庫連線: {error}', + 'CDbConnection is inactive and cannot perform any DB operations.' => 'CDbConnection 狀態為未啟用, 無法進行任何資料庫動作.', + 'CDbConnection.connectionString cannot be empty.' => 'CDbConnection.connectionString 不能是空的.', + 'CDbDataReader cannot rewind. It is a forward-only reader.' => 'CDbDataReader 無法倒回, 只允許向前讀取.', + 'CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.' => 'CDbHttpSession.connectionID "{id}" 是無效的. 請確定它參照到一個 CDbConnection 應用程式元件的 ID.', + 'CDbLogRoute requires database table "{table}" to store log messages.' => 'CDbLogRoute 要求資料庫 table "{table}" 儲存日誌訊息.', + 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.' => 'CDbLogRoute.connectionID "{id}" 未指向一個有效的 CDbConnection 應用程式元件.', + 'CDbMessageSource.connectionID is invalid. Please make sure "{id}" refers to a valid database application component.' => 'CDbMessageSource.connectionID 是無效的. 請確定 "{id}" 參照到一個有效的資料庫應用程式原件.', + 'CDbTransaction is inactive and cannot perform commit or roll back operations.' => 'CDbTransaction 狀態為未啟用, 無法進行 commit 或 roll back 動作.', + 'CDirectoryCacheDependency.directory cannot be empty.' => 'CDirectoryCacheDependency.directory 不能是空的.', + 'CFileCacheDependency.fileName cannot be empty.' => 'CFileCacheDependency.fileName 不能是空的.', + 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.' => 'CFileLogRoute.logPath "{path}" does not point to a valid directory. 請確定目錄存在並且允許 Web server process 寫入.', + 'CFilterChain can only take objects implementing the IFilter interface.' => 'CFilterChain 只能取得有實作 IFilter 介面的物件.', + 'CFlexWidget.baseUrl cannot be empty.' => 'CFlexWidget.baseUrl 不能是空的.', + 'CFlexWidget.name cannot be empty.' => 'CFlexWidget.name 不能是空的.', + 'CGlobalStateCacheDependency.stateName cannot be empty.' => 'CGlobalStateCacheDependency.stateName 不能是空的.', + 'CHttpCookieCollection can only hold CHttpCookie objects.' => 'CHttpCookieCollection 只能持有 CHttpCookie 物件.', + 'CHttpRequest is unable to determine the entry script URL.' => 'CHttpRequest 不能確認入口腳本的 URL.', + 'CHttpRequest is unable to determine the path info of the request.' => 'CHttpRequest 不能確認請求的路徑資訊.', + 'CHttpRequest is unable to determine the request URI.' => 'CHttpRequest 不能確認請求的 URI.', + 'CHttpSession.cookieMode can only be "none", "allow" or "only".' => 'CHttpSession.cookieMode 只能是 "none", "allow" 或 "only".', + 'CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.' => 'CHttpSession.gcProbability "{value}" 是無效的. 它必須是介於 0 與 100之間的整數.', + 'CHttpSession.savePath "{path}" is not a valid directory.' => 'CHttpSession.savePath "{path}" 不是一個有效的目錄.', + 'CMemCache requires PHP memcache extension to be loaded.' => 'CMemCache 要求 PHP memcache extension 必須先被載入.', + 'CMemCache server configuration must be an array.' => 'CMemCache 伺服器組態設定必須是一個陣列.', + 'CMemCache server configuration must have "host" value.' => 'CMemCache 伺服器組態設定必須有 "host" 的值.', + 'CMultiFileUpload.name is required.' => 'CMultiFileUpload.name 是必要的.', + 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.' => 'CProfileLogRoute found a mismatching code block "{token}". 請確定對於 Yii::beginProfile() 與 Yii::endProfile() 的呼叫是適當地巢狀套疊的.', + 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".' => 'CProfileLogRoute.report "{report}" 是無效的. 有效值包含 "summary" 與 "callstack".', + 'CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.' => 'CSecurityManager 要求 PHP mcrypt extension 必須先被載入以便使用資料加密功能.', + 'CSecurityManager.encryptionKey cannot be empty.' => 'CSecurityManager.encryptionKey 不能是空的.', + 'CSecurityManager.validation must be either "MD5" or "SHA1".' => 'CSecurityManager.validation 必須是 "MD5" 或 "SHA1".', + 'CSecurityManager.validationKey cannot be empty.' => 'CSecurityManager.validationKey 不能是空的.', + 'CTypedList<{type}> can only hold objects of {type} class.' => 'CTypedList<{type}> 只能持有 {type} 類別的物件.', + 'CUrlManager.UrlFormat must be either "path" or "get".' => 'CUrlManager.UrlFormat 必須是 "path" 或 "get".', + 'CXCache requires PHP XCache extension to be loaded.' => 'CXCache 要求 PHP XCache extension 必須先被載入.', + 'Cache table "{tableName}" does not exist.' => 'Cache table "{tableName}" 不存在.', + 'Cannot add "{child}" as a child of "{name}". A loop has been detected.' => '無法新增 "{child}" 成為 "{name}" 的子項. 偵測到有迴圈產生.', + 'Cannot add "{child}" as a child of "{parent}". A loop has been detected.' => '無法新增 "{child}" 成為 "{parent}" 的子項. 偵測到有迴圈產生.', + 'Cannot add "{name}" as a child of itself.' => '無法新增 "{name}" 成為它自己的子項.', + 'Cannot add an item of type "{child}" to an item of type "{parent}".' => '無法新增一個種類為 "{child}" 的項目到一個種類為 "{parent}" 的項目.', + 'Either "{parent}" or "{child}" does not exist.' => '"{parent}" 或 "{child}" 不存在.', + 'Error: Table "{table}" does not have a primary key.' => '錯誤: Table "{table}" 沒有定義主鍵.', + 'Error: Table "{table}" has a composite primary key which is not supported by crud command.' => '錯誤: Table "{table}" 有一個不被 crud 命令所支援的合成主鍵.', + 'Event "{class}.{event}" is attached with an invalid handler "{handler}".' => '事件 "{class}.{event}" 附加了一個無效的 handler "{handler}".', + 'Event "{class}.{event}" is not defined.' => '事件 "{class}.{event}" 未定義.', + 'Failed to write the uploaded file "{file}" to disk.' => '無法將已上傳的檔案 "{file}" 寫入磁碟.', + 'File upload was stopped by extension.' => '檔案上傳被 extension 所停止.', + 'Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".' => '篩選器 "{filter}" 是無效的. 控制器 "{class}" 沒有名為 "filter{filter}" 的篩選器方法.', + 'Get a new code' => '取得一組新代碼', + 'Invalid MO file revision: {revision}.' => '無效的 MO 檔案修訂: {revision}.', + 'Invalid MO file: {file} (magic: {magic}).' => '無效的 MO 檔案: {file} (magic: {magic}).', + 'Invalid enumerable value "{value}". Please make sure it is among ({enum}).' => '無效的 enumerable 值 "{value}". 請確定它在 ({enum}) 之中.', + 'List data must be an array or an object implementing Traversable.' => '表列資料必須是一個陣列或是有實作 Traversable 的一個物件.', + 'List index "{index}" is out of bound.' => '表列索引 "{index}" 超出範圍.', + 'Login Required' => '需要先登入系統', + 'Map data must be an array or an object implementing Traversable.' => '對照表資料必須是一個陣列或一個實作 Traversable 的物件.', + 'Missing the temporary folder to store the uploaded file "{file}".' => '缺乏暫存目錄來儲存已上傳的檔案 "{file}".', + 'No columns are being updated for table "{table}".' => 'table "{table}" 沒有任何欄位將被更新.', + 'No counter columns are being updated for table "{table}".' => 'table "{table}" 沒有任何計數器欄位將被更新.', + 'Object configuration must be an array containing a "class" element.' => '物件組態設定必須是內含有一個 "class" 元素的一個陣列.', + 'Please fix the following input errors:' => '請更正下列輸入錯誤:', + 'Property "{class}.{property}" is not defined.' => '屬性 "{class}.{property}" 未被定義.', + 'Property "{class}.{property}" is read only.' => '屬性 "{class}.{property}" 是唯讀的.', + 'Queue data must be an array or an object implementing Traversable.' => '佇列資料必須是一個陣列或一個實作 Traversable 的物件.', + 'Relation "{name}" is not defined in active record class "{class}".' => '關聯 "{name}" 未被定義在 active record class "{class}" 中.', + 'Stack data must be an array or an object implementing Traversable.' => '堆疊資料必須是一個陣列或一個實作 Traversable 的物件.', + 'Table "{table}" does not have a column named "{column}".' => 'Table "{table}" 沒有名為 "{column}" 的欄位.', + 'Table "{table}" does not have a primary key defined.' => 'Table "{table}" 沒有定義主鍵.', + 'The "filter" property must be specified with a valid callback.' => '屬性 "filter" 必須以一個有效的 callback 指明.', + 'The "pattern" property must be specified with a valid regular expression.' => '屬性 "pattern" 必須以一個有效的 regular expression 指明.', + 'The "view" property is required.' => '需要 "view" 屬性', + 'The CSRF token could not be verified.' => 'CSRF token 無法被驗證.', + 'The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.' => 'route "{route}" 中的 URL 樣式 "{pattern}" 不是有效的 regular expression.', + 'The active record cannot be deleted because it is new.' => 'active record 由於是新的, 無法被刪除.', + 'The active record cannot be inserted to database because it is not new.' => 'active record 由於不是新的, 無法被新增到資料庫.', + 'The active record cannot be updated because it is new.' => 'active record 由於是新的, 無法被更新.', + 'The asset "{asset}" to be pulished does not exist.' => '欲發佈的 asset "{asset}" 不存在.', + 'The column "{column}" is not a foreign key in table "{table}".' => '欄位 "{column}" 並不是 table "{table}" 中的一個 foreign key.', + 'The command path "{path}" is not a valid directory.' => '命令路徑 "{path}" 不是一個有效的目錄.', + 'The controller path "{path}" is not a valid directory.' => '控制器路徑 "{path}" 不是一個有效的目錄.', + 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.' => '檔案 "{file}" 無法被上傳. 只有附檔名如下的檔案是被允許的: {extensions}.', + 'The file "{file}" is too large. Its size cannot exceed {limit} bytes.' => '檔案 "{file}" 太大. 檔案大小不能超過 {limit} 位元組.', + 'The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.' => '檔案 "{file}" 太小. 檔案大小不能少於 {limit} 位元組.', + 'The file "{file}" was only partially uploaded.' => '檔案 "{file}" 上傳不完全.', + 'The first element in a filter configuration must be the filter class.' => '篩選器組態設定中的第一個元素必須是篩選器類別.', + 'The item "{name}" does not exist.' => '項目 "{name}" 不存在.', + 'The item "{parent}" already has a child "{child}".' => '項目 "{parent}" 已有子項目 "{child}".', + 'The layout path "{path}" is not a valid directory.' => '佈局路徑 "{path}" 不是一個有效的目錄.', + 'The list is read only.' => '表列是唯讀的.', + 'The map is read only.' => '對照表是唯讀的.', + 'The pattern for 12 hour format must be "h" or "hh".' => '代表12小時制的樣式必須是 "h" 或 "hh".', + 'The pattern for 24 hour format must be "H" or "HH".' => '代表24小時制的樣式必須是 "H" 或 "HH".', + 'The pattern for AM/PM marker must be "a".' => '代表 AM/PM 標記的樣式必須是 "a".', + 'The pattern for day in month must be "F".' => '代表 day in month 的樣式必須是 "F".', + 'The pattern for day in year must be "D", "DD" or "DDD".' => '代表 day in year 的樣式必須是 "D", "DD" 或 "DDD".', + 'The pattern for day of the month must be "d" or "dd".' => '代表 day of the month 的樣式必須是 "d" 或 "dd".', + 'The pattern for day of the week must be "E", "EE", "EEE", "EEEE" or "EEEEE".' => '代表 day of the week 的樣式必須是 "E", "EE", "EEE", "EEEE" 或 "EEEEE".', + 'The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".' => '代表年代的樣式必須是 "G", "GG", "GGG", "GGGG" 或 "GGGGG".', + 'The pattern for hour in AM/PM must be "K" or "KK".' => '代表時(AM/PM格式)的樣式必須是 "K" 或 "KK".', + 'The pattern for hour in day must be "k" or "kk".' => '代表該天第幾小時的樣式必須是 "k" 或 "kk".', + 'The pattern for minutes must be "m" or "mm".' => '代表分的樣式必須是 "m" 或 "mm".', + 'The pattern for month must be "M", "MM", "MMM", or "MMMM".' => '代表月份的樣式必須是 "M", "MM", "MMM", 或 "MMMM".', + 'The pattern for seconds must be "s" or "ss".' => '代表秒的樣式必須是 "s" 或 "ss".', + 'The pattern for time zone must be "z" or "v".' => '代表時區的樣式必須是 "z" 或 "v".', + 'The pattern for week in month must be "W".' => '代表 week in month 的樣式必須是 "W".', + 'The pattern for week in year must be "w".' => '代表 week in year 的樣式必須是 "w".', + 'The queue is empty.' => '佇列狀態為空.', + 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.' => 'active record class "{class}" 中的關聯 "{relation}" 未被正確指明: 資料庫中無法找到 foreign key 中所給的 join table "{joinTable}".', + 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.' => 'active record class "{class}" 中的關聯 "{relation}" 有一個不完整的 foreign key. foreign key 必須是參照 joining tables 中的欄位所構成.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.' => 'active record class "{class}" 中的關聯 "{relation}" 有一個無效的 foreign key "{key}". foreign key 未指到任一個 joining table.', + 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".' => 'active record class "{class}" 中的關聯 "{relation}" 有一個無效的 foreign key. foreign key 的格式必須是 "joinTable(fk1,fk2,...)".', + 'The requested controller "{controller}" does not exist.' => '請求的控制器 "{controller}" 不存在.', + 'The requested view "{name}" is not found.' => '請求的 view "{name}" 未找到.', + 'The stack is empty.' => '堆疊狀態為空.', + 'The system is unable to find the requested action "{action}".' => '系統無法找到請求的 "{action}" 動作.', + 'The system view path "{path}" is not a valid directory.' => '系統 view 路徑 "{path}" 不是一個有效的目錄.', + 'The table "{table}" for active record class "{class}" cannot be found in the database.' => '資料庫中無法找到 active record class "{class}" 對應的 table "{table}".', + 'The value for the primary key "{key}" is not supplied when querying the table "{table}".' => '查詢 table "{table}" 時未提供 primary key "{key}" 的值.', + 'The verification code is incorrect.' => '驗證碼不正確.', + 'The view path "{path}" is not a valid directory.' => 'view 路徑 "{path}" 不是一個有效的目錄.', + 'Theme directory "{directory}" does not exist.' => 'Theme 目錄 "{directory}" 不存在.', + 'This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.' => '內容需有 <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.', + 'Unable to add an item whose name is the same as an existing item.' => '無法新增與已存在項目名稱相同的新項目.', + 'Unable to change the item name. The name "{name}" is already used by another item.' => '無法變更項目名稱. 名稱 "{name}" 已被其它項目使用.', + 'Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.' => '無法產生應用程式狀態檔案 "{file}". 請確認存放此檔案的目錄存在並且允許 Web server process 寫入.', + 'Unable to find the decorator view "{view}".' => '無法找到 decorator view "{view}".', + 'Unable to find the list item.' => '無法找到表列項目.', + 'Unable to lock file "{file}" for reading.' => '無法封鎖檔案 "{file}" 進行讀取.', + 'Unable to lock file "{file}" for writing.' => '無法封鎖檔案 "{file}" 進行寫入.', + 'Unable to read file "{file}".' => '無法讀取檔案 "{file}".', + 'Unable to replay the action "{object}.{method}". The method does not exist.' => '無法再次重演 "{object}.{method}" 動作. 這個方法不存在.', + 'Unable to write file "{file}".' => '無法寫入檔案 "{file}".', + 'Unknown authorization item "{name}".' => '未知的授權項目 "{name}".', + 'Unrecognized locale "{locale}".' => '無法辨識的地區設定 "{locale}".', + 'View file "{file}" does not exist.' => '名為 "{file}" 的 View 檔不存在.', + 'Yii application can only be created once.' => 'Yii 應用程式只能被產生一次.', + 'You are not authorized to perform this action.' => '您未被授權執行這個動作', + 'Your request is not valid.' => '您的請求無效', + '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" 已被取用.', + '{attribute} cannot be blank.' => '{attribute} 不可為空白.', + '{attribute} is invalid.' => '{attribute} 無效.', + '{attribute} is not a valid URL.' => '{attribute} 不是有效的 URL.', + '{attribute} is not a valid email address.' => '{attribute} 不是有效的電子郵件地址.', + '{attribute} is not in the list.' => '{attribute} 不在表列之中.', + '{attribute} is of the wrong length (should be {length} characters).' => '{attribute} 長度錯誤 (應為 {length} 字元).', + '{attribute} is too big (maximum is {max}).' => '{attribute} 數值太大 (最大值為 {max}).', + '{attribute} is too long (maximum is {max} characters).' => '{attribute} 太長 (最大值為 {max} 字元).', + '{attribute} is too short (minimum is {min} characters).' => '{attribute} 太短 (最小值為 {min} 字元).', + '{attribute} is too small (minimum is {min}).' => '{attribute} 數值太小 (最小值為 {min}).', + '{attribute} must be a number.' => '{attribute} 必須為數字.', + '{attribute} must be an integer.' => '{attribute} 必須為整數.', + '{attribute} must be repeated exactly.' => '{attribute} 必須被重覆.', + '{attribute} must be {type}.' => '{attribute} 必須為 {type}.', + '{className} does not support add() functionality.' => '{className} 不支援 add() 功能.', + '{className} does not support delete() functionality.' => '{className} 不支援 delete() 功能.', + '{className} does not support flush() functionality.' => '{className} 不支援 flush() 功能.', + '{className} does not support get() functionality.' => '{className} 不支援 get() 功能.', + '{className} does not support set() functionality.' => '{className} 不支援 set() 功能.', + '{class} does not have attribute "{name}".' => '{class} 中沒有名為 "{name}" 的屬性.', + '{class} does not have relation "{name}".' => '{class} 中沒有名為 "{name}" 的關聯.', + '{class} does not support fetching all table names.' => '{class} 不支援擷取所有 table 名稱.', + '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.' => '{class} 有一個無效的確認規則. 規則必須指明要被確認的屬性以及確認器名稱.', + '{class} must specify "model" and "attribute" or "name" property values.' => '{class} 必須給定 "model" 與 "attribute" 或 "name" 屬性值.', + '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.' => '{class}.allowAutoLogin 必須設為 true 才能使用 cookie-based 認證.', + '{class}::authenticate() must be implemented.' => '{class}::authenticate() 必須被實作.', + '{controller} cannot find the requested view "{view}".' => '{controller} 無法找到請求的 "{view}" view.', + '{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.' => '{controller} 在它的 view "{view}" 中含有未被適當巢狀套疊的 widget 標籤. {widget} widget 中沒有呼叫 endWidget().', + '{controller} has an extra endWidget({id}) call in its view.' => '{controller} 在它的 view 中有一個額外的 endWidget({id}) 呼叫.', + '{widget} cannot find the view "{view}".' => '{widget} 無法找到這個 view "{view}".', +); diff --git a/web/framework-1.0.8/utils/CDateTimeParser.php b/web/framework-1.0.8/utils/CDateTimeParser.php new file mode 100644 index 0000000..7f8eceb --- /dev/null +++ b/web/framework-1.0.8/utils/CDateTimeParser.php @@ -0,0 +1,230 @@ +<?php +/** + * CDateTimeParser class file + * + * @author Wei Zhuo <weizhuo[at]gamil[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @author Tomasz Suchanek <tomasz[dot]suchanek[at]gmail[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDateTimeParser converts a date/time string to a UNIX timestamp according to the specified pattern. + * + * The following pattern characters are recognized: + * <pre> + * Pattern | Description + * ---------------------------------------------------- + * d | Day of month 1 to 31, no padding + * dd | Day of monath 01 to 31, zero leading + * M | Month digit 1 to 12, no padding + * MM | Month digit 01 to 12, zero leading + * yy | 2 year digit, e.g., 96, 05 + * yyyy | 4 year digit, e.g., 2005 + * h | Hour in 0 to 23, no padding (since version 1.0.5) + * hh | Hour in 00 to 23, zero leading (since version 1.0.5) + * m | Minutes in 0 to 59, no padding (since version 1.0.5) + * mm | Minutes in 00 to 59, zero leading (since version 1.0.5) + * s | Seconds in 0 to 59, no padding (since version 1.0.5) + * ss | Seconds in 00 to 59, zero leading (since version 1.0.5) + * ---------------------------------------------------- + * </pre> + * All other characters must appear in the date string at the corresponding positions. + * + * For example, to parse a date string '21/10/2008', use the following: + * <pre> + * $timestamp=CDateTimeParser::parse('21/10/2008','dd/MM/yyyy'); + * </pre> + * + * To format a timestamp to a date string, please use {@link CDateFormatter}. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDateTimeParser.php 1199 2009-07-04 13:48:08Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CDateTimeParser +{ + /** + * Converts a date string to a timestamp. + * @param string the date string to be parsed + * @param string the pattern that the date string is following + * @return integer timestamp for the date string. False if parsing fails. + */ + public static function parse($value,$pattern='MM/dd/yyyy') + { + $tokens=self::tokenize($pattern); + $i=0; + $n=strlen($value); + foreach($tokens as $token) + { + switch($token) + { + case 'yyyy': + { + if(($year=self::parseInteger($value,$i,4,4))===false) + return false; + $i+=4; + break; + } + case 'yy': + { + if(($year=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($year); + break; + } + case 'MM': + { + if(($month=self::parseInteger($value,$i,2,2))===false) + return false; + $i+=2; + break; + } + case 'M': + { + if(($month=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($month); + break; + } + case 'dd': + { + if(($day=self::parseInteger($value,$i,2,2))===false) + return false; + $i+=2; + break; + } + case 'd': + { + if(($day=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($day); + break; + } + case 'h': + { + if(($hour=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($hour); + break; + } + case 'hh': + { + if(($hour=self::parseInteger($value,$i,2,2))===false) + return false; + $i+=2; + break; + } + case 'm': + { + if(($minute=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($minute); + break; + } + case 'mm': + { + if(($minute=self::parseInteger($value,$i,2,2))===false) + return false; + $i+=2; + break; + } + case 's': + { + if(($second=self::parseInteger($value,$i,1,2))===false) + return false; + $i+=strlen($second); + break; + } + case 'ss': + { + if(($second=self::parseInteger($value,$i,2,2))===false) + return false; + $i+=2; + break; + } + default: + { + $tn=strlen($token); + if($i>=$n || substr($value,$i,$tn)!==$token) + return false; + $i+=$tn; + break; + } + } + } + if($i<$n) + return false; + + if(!isset($year)) + $year=date('Y'); + if(!isset($month)) + $month=date('n'); + if(!isset($day)) + $day=date('j'); + + if(strlen($year)===2) + { + if($year>70) + $year+=1900; + else + $year+=2000; + } + $year=(int)$year; + $month=(int)$month; + $day=(int)$day; + + if(!isset($hour) && !isset($minute) && !isset($second)) + $hour=$minute=$second=0; + else + { + if(!isset($hour)) + $hour=date('H'); + if(!isset($minute)) + $minute=date('i'); + if(!isset($second)) + $second=date('s'); + $hour=(int)$hour; + $minute=(int)$minute; + $second=(int)$second; + } + + if(CTimestamp::isValidDate($year,$month,$day) && CTimestamp::isValidTime($hour,$minute,$second)) + return CTimestamp::getTimestamp($hour,$minute,$second,$month,$day,$year); + else + return false; + } + + private static function tokenize($pattern) + { + if(!($n=strlen($pattern))) + return array(); + $tokens=array(); + for($c0=$pattern[0],$start=0,$i=1;$i<$n;++$i) + { + if(($c=$pattern[$i])!==$c0) + { + $tokens[]=substr($pattern,$start,$i-$start); + $c0=$c; + $start=$i; + } + } + $tokens[]=substr($pattern,$start,$n-$start); + return $tokens; + } + + protected static function parseInteger($value,$offset,$minLength,$maxLength) + { + for($len=$maxLength;$len>=$minLength;--$len) + { + $v=substr($value,$offset,$len); + if(ctype_digit($v)) + return $v; + } + return false; + } +} diff --git a/web/framework-1.0.8/utils/CFileHelper.php b/web/framework-1.0.8/utils/CFileHelper.php new file mode 100644 index 0000000..5fe1080 --- /dev/null +++ b/web/framework-1.0.8/utils/CFileHelper.php @@ -0,0 +1,228 @@ +<?php +/** + * CFileHelper class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileHelper provides a set of helper methods for common file system operations. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileHelper.php 1246 2009-07-15 18:39:18Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CFileHelper +{ + /** + * Copies a directory recursively as another. + * If the destination directory does not exist, it will be created. + * @param string the source directory + * @param string the destination directory + * @param array options for directory copy. Valid options are: + * <ul> + * <li>fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be copied.</li> + * <li>exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'.</li> + * <li>level: integer, recursion depth, default=-1. + * Level -1 means copying all directories and files under the directory; + * Level 0 means copying only the files DIRECTLY under the directory; + * level N means copying those directories that are within N levels. + * </li> + * </ul> + */ + public static function copyDirectory($src,$dst,$options=array()) + { + $fileTypes=array(); + $exclude=array(); + $level=-1; + extract($options); + self::copyDirectoryRecursive($src,$dst,'',$fileTypes,$exclude,$level); + } + + /** + * Returns the files found under the specified directory and subdirectories. + * @param string the directory under which the files will be looked for + * @param array options for file searching. Valid options are: + * <ul> + * <li>fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned.</li> + * <li>exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'.</li> + * <li>level: integer, recursion depth, default=-1. + * Level -1 means searching for all directories and files under the directory; + * Level 0 means searching for only the files DIRECTLY under the directory; + * level N means searching for those directories that are within N levels. + * </li> + * </ul> + * @return array files found under the directory. The file list is sorted. + */ + public static function findFiles($dir,$options=array()) + { + $fileTypes=array(); + $exclude=array(); + $level=-1; + extract($options); + $list=self::findFilesRecursive($dir,'',$fileTypes,$exclude,$level); + sort($list); + return $list; + } + + /** + * Copies a directory. + * This method is mainly used by {@link copyDirectory}. + * @param string the source directory + * @param string the destination directory + * @param string the path relative to the original source directory + * @param array list of file name suffix (without dot). Only files with these suffixes will be copied. + * @param array list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. + * @param integer recursion depth. It defaults to -1. + * Level -1 means copying all directories and files under the directory; + * Level 0 means copying only the files DIRECTLY under the directory; + * level N means copying those directories that are within N levels. + */ + protected static function copyDirectoryRecursive($src,$dst,$base,$fileTypes,$exclude,$level) + { + @mkdir($dst); + @chmod($dst,0777); + $folder=opendir($src); + while($file=readdir($folder)) + { + if($file==='.' || $file==='..') + continue; + $path=$src.DIRECTORY_SEPARATOR.$file; + $isFile=is_file($path); + if(self::validatePath($base,$file,$isFile,$fileTypes,$exclude)) + { + if($isFile) + copy($path,$dst.DIRECTORY_SEPARATOR.$file); + else if($level) + self::copyDirectoryRecursive($path,$dst.DIRECTORY_SEPARATOR.$file,$base.'/'.$file,$fileTypes,$exclude,$level-1); + } + } + closedir($folder); + } + + /** + * Returns the files found under the specified directory and subdirectories. + * This method is mainly used by {@link findFiles}. + * @param string the source directory + * @param string the path relative to the original source directory + * @param array list of file name suffix (without dot). Only files with these suffixes will be returned. + * @param array list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. + * @param integer recursion depth. It defaults to -1. + * Level -1 means searching for all directories and files under the directory; + * Level 0 means searching for only the files DIRECTLY under the directory; + * level N means searching for those directories that are within N levels. + * @return array files found under the directory. + */ + protected static function findFilesRecursive($dir,$base,$fileTypes,$exclude,$level) + { + $list=array(); + $handle=opendir($dir); + while($file=readdir($handle)) + { + if($file==='.' || $file==='..') + continue; + $path=$dir.DIRECTORY_SEPARATOR.$file; + $isFile=is_file($path); + if(self::validatePath($base,$file,$isFile,$fileTypes,$exclude)) + { + if($isFile) + $list[]=$path; + else if($level) + $list=array_merge($list,self::findFilesRecursive($path,$base.'/'.$file,$fileTypes,$exclude,$level-1)); + } + } + closedir($handle); + return $list; + } + + /** + * Validates a file or directory. + * @param string the path relative to the original source directory + * @param string the file or directory name + * @param boolean whether this is a file + * @param array list of file name suffix (without dot). Only files with these suffixes will be copied. + * @param array list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. + * @return boolean whether the file or directory is valid + */ + protected static function validatePath($base,$file,$isFile,$fileTypes,$exclude) + { + foreach($exclude as $e) + { + if($file===$e || strpos($base.'/'.$file,$e)===0) + return false; + } + if(!$isFile || empty($fileTypes)) + return true; + if(($pos=strrpos($file,'.'))!==false) + { + $type=substr($file,$pos+1); + return in_array($type,$fileTypes); + } + else + return false; + } + + /** + * Determines the MIME type of the specified file. + * This method will attempt the following approaches in order: + * <ol> + * <li>finfo</li> + * <li>mime_content_type</li> + * <li>{@link getMimeTypeByExtension}</li> + * </ol> + * @param string the file name. + * @return string the MIME type. Null is returned if the MIME type cannot be determined. + */ + public static function getMimeType($file) + { + if(function_exists('finfo_open')) + { + if(($info=finfo_open(FILEINFO_MIME)) && ($result=finfo_file($info,$file))!==false) + return $result; + } + + if(function_exists('mime_content_type') && ($result=mime_content_type($file))!==false) + return $result; + + return self::getMimeTypeByExtension($file); + } + + /** + * Determines the MIME type based on the extension name of the specified file. + * This method will use a local map between extension name and MIME type. + * @param string the file name. + * @return string the MIME type. Null is returned if the MIME type cannot be determined. + */ + public static function getMimeTypeByExtension($file) + { + static $extensions; + if($extensions===null) + $extensions=require(Yii::getPathOfAlias('system.utils.mimeTypes').'.php'); + if(($pos=strrpos($file,'.'))!==false) + { + $ext=strtolower(substr($file,$pos+1)); + if(isset($extensions[$ext])) + return $extensions[$ext]; + } + return null; + } +} diff --git a/web/framework-1.0.8/utils/CMarkdownParser.php b/web/framework-1.0.8/utils/CMarkdownParser.php new file mode 100644 index 0000000..00147fd --- /dev/null +++ b/web/framework-1.0.8/utils/CMarkdownParser.php @@ -0,0 +1,179 @@ +<?php +/** + * CMarkdownParser class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +require_once(Yii::getPathOfAlias('system.vendors.markdown.markdown').'.php'); + +/** + * CMarkdownParser is a wrapper of {@link http://michelf.com/projects/php-markdown/extra/ MarkdownExtra_Parser}. + * + * CMarkdownParser extends MarkdownExtra_Parser by using Text_Highlighter + * to highlight code blocks with specific language syntax. + * In particular, if a code block starts with the following: + * <pre> + * [language] + * </pre> + * The syntax for the specified language will be used to highlight + * code block. The languages supported include (case-insensitive): + * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, + * MYSQL, PERL, PHP, PYTHON, RUBY, SQL, XML + * + * You can also specify options to be passed to the syntax highlighter. For example: + * <pre> + * [php showLineNumbers=1] + * </pre> + * which will show line numbers in each line of the code block. + * + * For details about the standard markdown syntax, please check the following: + * <ul> + * <li>{@link http://daringfireball.net/projects/markdown/syntax official markdown syntax}</li> + * <li>{@link http://michelf.com/projects/php-markdown/extra/ markdown extra syntax}</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CMarkdownParser.php 443 2009-01-01 23:11:39Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CMarkdownParser extends MarkdownExtra_Parser +{ + /** + * @var string the css class for the div element containing + * the code block that is highlighted. Defaults to 'hl-code'. + */ + public $highlightCssClass='hl-code'; + + /** + * Transforms the content and purifies the result. + * This method calls the transform() method to convert + * markdown content into HTML content. It then + * uses {@link CHtmlPurifier} to purify the HTML content + * to avoid XSS attacks. + * @param string the markdown content + * @return string the purified HTML content + * @since 1.0.1 + */ + public function safeTransform($content) + { + $content=$this->transform($content); + $purifier=new CHtmlPurifier; + return $purifier->purify($content); + } + + /** + * @return string the default CSS file that is used to highlight code blocks. + */ + public function getDefaultCssFile() + { + return Yii::getPathOfAlias('system.vendors.TextHighlighter.highlight').'.css'; + } + + /** + * Callback function when a code block is matched. + * @param array matches + * @return string the highlighted code block + */ + public function _doCodeBlocks_callback($matches) + { + $codeblock = $this->outdent($matches[1]); + if(($codeblock = $this->highlightCodeBlock($codeblock)) !== null) + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + else + return parent::_doCodeBlocks_callback($matches); + } + + /** + * Callback function when a fenced code block is matched. + * @param array matches + * @return string the highlighted code block + */ + public function _doFencedCodeBlocks_callback($matches) + { + return "\n\n".$this->hashBlock($this->highlightCodeBlock($matches[2]))."\n\n"; + } + + /** + * Highlights the code block. + * @param string the code block + * @return string the highlighted code block. Null if the code block does not need to highlighted + */ + protected function highlightCodeBlock($codeblock) + { + if(($tag=$this->getHighlightTag($codeblock))!==null && ($highlighter=$this->createHighLighter($tag))) + { + $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock); + $tagLen = strpos($codeblock, $tag)+strlen($tag); + $codeblock = ltrim(substr($codeblock, $tagLen)); + $output=preg_replace('/<span\s+[^>]*>(\s*)<\/span>/', '\1', $highlighter->highlight($codeblock)); + return "<div class=\"{$this->highlightCssClass}\">".$output."</div>"; + } + else + return "<pre>".$codeblock."</pre>"; + } + + /** + * Returns the user-entered highlighting options. + * @param string code block with highlighting options. + * @return string the user-entered highlighting options. Null if no option is entered. + */ + protected function getHighlightTag($codeblock) + { + $str = trim(current(preg_split("/\r|\n/", $codeblock,2))); + if(strlen($str) > 2 && $str[0] === '[' && $str[strlen($str)-1] === ']') + return $str; + } + + /** + * Creates a highlighter instance. + * @param string the user-entered options + * @return Text_Highlighter the highlighter instance + */ + protected function createHighLighter($options) + { + if(!class_exists('Text_Highlighter', false)) + { + require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter').'.php'); + require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter.Renderer.Html').'.php'); + } + $lang = current(preg_split('/\s+/', substr(substr($options,1), 0,-1),2)); + $highlighter = Text_Highlighter::factory($lang); + if($highlighter) + $highlighter->setRenderer(new Text_Highlighter_Renderer_Html($this->getHiglightConfig($options))); + return $highlighter; + } + + /** + * Generates the config for the highlighter. + * @param string user-entered options + * @return array the highlighter config + */ + public function getHiglightConfig($options) + { + $config['use_language'] = true; + if( $this->getInlineOption('showLineNumbers', $options, false) ) + $config['numbers'] = HL_NUMBERS_LI; + $config['tabsize'] = $this->getInlineOption('tabSize', $options, 4); + return $config; + } + + /** + * Retrieves the specified configuration. + * @param string the configuration name + * @param string the user-entered options + * @param mixed default value if the configuration is not present + * @return mixed the configuration value + */ + protected function getInlineOption($name, $str, $defaultValue) + { + if(preg_match('/'.$name.'(\s*=\s*(\d+))?/i', $str, $v) && count($v) > 2) + return $v[2]; + else + return $defaultValue; + } +} diff --git a/web/framework-1.0.8/utils/CPropertyValue.php b/web/framework-1.0.8/utils/CPropertyValue.php new file mode 100644 index 0000000..10ba695 --- /dev/null +++ b/web/framework-1.0.8/utils/CPropertyValue.php @@ -0,0 +1,158 @@ +<?php +/** + * CPropertyValue class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPropertyValue is a helper class that provides static methods to convert component property values to specific types. + * + * CPropertyValue is commonly used in component setter methods to ensure + * the new property value is of the specific type. + * For example, a boolean-typed property setter method would be as follows, + * <pre> + * public function setPropertyName($value) + * { + * $value=CPropertyValue::ensureBoolean($value); + * // $value is now of boolean type + * } + * </pre> + * + * Properties can be of the following types with specific type conversion rules: + * <ul> + * <li>string: a boolean value will be converted to 'true' or 'false'.</li> + * <li>boolean: string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false.</li> + * <li>integer</li> + * <li>float</li> + * <li>array: string starting with '(' and ending with ')' will be considered as + * as an array expression and will be evaluated. Otherwise, an array + * with the value to be ensured is returned.</li> + * <li>object</li> + * <li>enum: enumerable type, represented by an array of strings.</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CPropertyValue.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CPropertyValue +{ + /** + * Converts a value to boolean type. + * Note, string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false. + * If a string represents a non-zero number, it will be treated as true. + * @param mixed the value to be converted. + * @return boolean + */ + public static function ensureBoolean($value) + { + if (is_string($value)) + return !strcasecmp($value,'true') || $value!=0; + else + return (boolean)$value; + } + + /** + * Converts a value to string type. + * Note, a boolean value will be converted to 'true' if it is true + * and 'false' if it is false. + * @param mixed the value to be converted. + * @return string + */ + public static function ensureString($value) + { + if (is_bool($value)) + return $value?'true':'false'; + else + return (string)$value; + } + + /** + * Converts a value to integer type. + * @param mixed the value to be converted. + * @return integer + */ + public static function ensureInteger($value) + { + return (integer)$value; + } + + /** + * Converts a value to float type. + * @param mixed the value to be converted. + * @return float + */ + public static function ensureFloat($value) + { + return (float)$value; + } + + /** + * Converts a value to array type. If the value is a string and it is + * in the form (a,b,c) then an array consisting of each of the elements + * will be returned. If the value is a string and it is not in this form + * then an array consisting of just the string will be returned. If the value + * is not a string then + * @param mixed the value to be converted. + * @return array + */ + public static function ensureArray($value) + { + if(is_string($value)) + { + $value = trim($value); + $len = strlen($value); + if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')') + { + eval('$array=array'.$value.';'); + return $array; + } + else + return $len>0?array($value):array(); + } + else + return (array)$value; + } + + /** + * Converts a value to object type. + * @param mixed the value to be converted. + * @return object + */ + public static function ensureObject($value) + { + return (object)$value; + } + + /** + * Converts a value to enum type. + * + * This method checks if the value is of the specified enumerable type. + * A value is a valid enumerable value if it is equal to the name of a constant + * in the specified enumerable type (class). + * For more details about enumerable, see {@link CEnumerable}. + * + * @param string the enumerable value to be checked. + * @param string the enumerable class name (make sure it is included before calling this function). + * @return string the valid enumeration value + * @throws CException if the value is not a valid enumerable value + */ + public static function ensureEnum($value,$enumType) + { + static $types=array(); + if(!isset($types[$enumType])) + $types[$enumType]=new ReflectionClass($enumType); + if($types[$enumType]->hasConstant($value)) + return $value; + else + throw new CException(Yii::t('yii','Invalid enumerable value "{value}". Please make sure it is among ({enum}).', + array('{value}'=>$value, '{enum}'=>implode(', ',$types[$enumType]->getConstants())))); + } +} diff --git a/web/framework-1.0.8/utils/CTimestamp.php b/web/framework-1.0.8/utils/CTimestamp.php new file mode 100644 index 0000000..466fd7a --- /dev/null +++ b/web/framework-1.0.8/utils/CTimestamp.php @@ -0,0 +1,715 @@ +<?php +/** + * CTimestamp class file. + * + * @author Wei Zhuo <weizhuo[at]gamil[dot]com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTimestamp represents a timestamp. + * + * This class was adapted from the ADOdb Date Library, as part of + * the {@link http://phplens.com/phpeverywhere/ ADOdb abstraction library}. + * The original source code was released under both BSD and GNU Lesser GPL + * library license, with the following copyright notice: + * Copyright (c) 2000, 2001, 2002, 2003, 2004 John Lim + * All rights reserved. + * + * PHP native date static functions use integer timestamps for computations. + * Because of this, dates are restricted to the years 1901-2038 on Unix + * and 1970-2038 on Windows due to integer overflow for dates beyond + * those years. This library overcomes these limitations by replacing the + * native static function's signed integers (normally 32-bits) with PHP floating + * point numbers (normally 64-bits). + * + * Dates from 100 A.D. to 3000 A.D. and later have been tested. The minimum + * is 100 A.D. as <100 will invoke the 2 => 4 digit year conversion. + * The maximum is billions of years in the future, but this is a theoretical + * limit as the computation of that year would take too long with the + * current implementation of {@link getTimestamp}. + * + * PERFORMANCE + * For high speed, this library uses the native date static functions where + * possible, and only switches to PHP code when the dates fall outside + * the 32-bit signed integer range. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id: CTimestamp.php 1052 2009-05-22 21:51:48Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CTimestamp +{ + private static $_monthNormal=array("",31,28,31,30,31,30,31,31,30,31,30,31); + private static $_monthLeaf=array("",31,29,31,30,31,30,31,31,30,31,30,31); + + /** + * Gets day of week, 0 = Sunday,... 6=Saturday. + * Algorithm from PEAR::Date_Calc + * @param integer year + * @param integer month + * @param integer day + * @return integer day of week + */ + public static function getDayofWeek($year, $month, $day) + { + /* + Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and + proclaimed that from that time onwards 3 days would be dropped from the calendar + every 400 years. + + Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). + */ + if ($year <= 1582) + { + if ($year < 1582 || + ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) + { + $greg_correction = 3; + } + else + { + $greg_correction = 0; + } + } + else + { + $greg_correction = 0; + } + + if($month > 2) + $month -= 2; + else + { + $month += 10; + $year--; + } + + $day = floor((13 * $month - 1) / 5) + + $day + ($year % 100) + + floor(($year % 100) / 4) + + floor(($year / 100) / 4) - 2 * + floor($year / 100) + 77 + $greg_correction; + + return $day - 7 * floor($day / 7); + } + + /** + * Checks for leap year, returns true if it is. No 2-digit year check. Also + * handles julian calendar correctly. + * @param integer year to check + * @return boolean true if is leap year + */ + public static function isLeapYear($year) + { + $year = self::digitCheck($year); + if ($year % 4 != 0) + return false; + + if ($year % 400 == 0) + return true; + // if gregorian calendar (>1582), century not-divisible by 400 is not leap + else if ($year > 1582 && $year % 100 == 0 ) + return false; + return true; + } + + /** + * Fix 2-digit years. Works for any century. + * Assumes that if 2-digit is more than 30 years in future, then previous century. + * @param integer year + * @return integer change two digit year into multiple digits + */ + protected static function digitCheck($y) + { + if ($y < 100){ + $yr = (integer) date("Y"); + $century = (integer) ($yr /100); + + if ($yr%100 > 50) { + $c1 = $century + 1; + $c0 = $century; + } else { + $c1 = $century; + $c0 = $century - 1; + } + $c1 *= 100; + // if 2-digit year is less than 30 years in future, set it to this century + // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. + if (($y + $c1) < $yr+30) $y = $y + $c1; + else $y = $y + $c0*100; + } + return $y; + } + + /** + * Returns 4-digit representation of the year. + * @param integer year + * @return integer 4-digit representation of the year + */ + public static function get4DigitYear($y) + { + return self::digitCheck($y); + } + + /** + * @return integer get local time zone offset from GMT + */ + public static function getGMTDiff() + { + static $TZ; + if (isset($TZ)) return $TZ; + + $TZ = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970); + return $TZ; + } + + /** + * Returns the getdate() array. + * @param integer original date timestamp. False to use the current timestamp. + * @param boolean false to compute the day of the week, default is true + * @param boolean true to calculate the GMT dates + * @return array an array with date info. + */ + public static function getDate($d=false,$fast=false,$gmt=false) + { + if ($d === false) return getdate(); + // check if number in 32-bit signed range + if ((abs($d) <= 0x7FFFFFFF)) + { + if ($d >= 0) // if windows, must be +ve integer + return @getdate($d); + } + return self::getDateInternal($d,$fast,$gmt); + } + + /** + * Low-level static function that returns the getdate() array. We have a special + * $fast flag, which if set to true, will return fewer array values, + * and is much faster as it does not calculate dow, etc. + * @param float original date + * @param boolean false to compute the day of the week, default is true + * @param boolean true to calculate the GMT dates + * @return array an array with date info. + */ + private static function getDateInternal($origd=false,$fast=true,$is_gmt=false) + { + static $YRS; + + $d = $origd - ($is_gmt ? 0 : self::getGMTDiff()); + + $_day_power = 86400; + $_hour_power = 3600; + $_min_power = 60; + + if ($d < -12219321600) + $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction + + $_month_table_normal =& self::$_monthNormal; + $_month_table_leaf = & self::$_monthLeaf; + + $d366 = $_day_power * 366; + $d365 = $_day_power * 365; + $month = $ndays = 0; + + if ($d < 0) + { + if (empty($YRS)) + $YRS = array( + 1970 => 0, + 1960 => -315619200, + 1950 => -631152000, + 1940 => -946771200, + 1930 => -1262304000, + 1920 => -1577923200, + 1910 => -1893456000, + 1900 => -2208988800, + 1890 => -2524521600, + 1880 => -2840140800, + 1870 => -3155673600, + 1860 => -3471292800, + 1850 => -3786825600, + 1840 => -4102444800, + 1830 => -4417977600, + 1820 => -4733596800, + 1810 => -5049129600, + 1800 => -5364662400, + 1790 => -5680195200, + 1780 => -5995814400, + 1770 => -6311347200, + 1760 => -6626966400, + 1750 => -6942499200, + 1740 => -7258118400, + 1730 => -7573651200, + 1720 => -7889270400, + 1710 => -8204803200, + 1700 => -8520336000, + 1690 => -8835868800, + 1680 => -9151488000, + 1670 => -9467020800, + 1660 => -9782640000, + 1650 => -10098172800, + 1640 => -10413792000, + 1630 => -10729324800, + 1620 => -11044944000, + 1610 => -11360476800, + 1600 => -11676096000); + + if ($is_gmt) + $origd = $d; + // The valid range of a 32bit signed timestamp is typically from + // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT + // + + # old algorithm iterates through all years. new algorithm does it in + # 10 year blocks + + /* + # old algo + for ($a = 1970 ; --$a >= 0;) { + $lastd = $d; + + if ($leaf = _adodb_is_leap_year($a)) $d += $d366; + else $d += $d365; + + if ($d >= 0) { + $year = $a; + break; + } + } + */ + + $lastsecs = 0; + $lastyear = 1970; + foreach($YRS as $year => $secs) + { + if ($d >= $secs) + { + $a = $lastyear; + break; + } + $lastsecs = $secs; + $lastyear = $year; + } + + $d -= $lastsecs; + if (!isset($a)) $a = $lastyear; + + //echo ' yr=',$a,' ', $d,'.'; + + for (; --$a >= 0;) + { + $lastd = $d; + + if ($leaf = self::isLeapYear($a)) + $d += $d366; + else + $d += $d365; + + if ($d >= 0) + { + $year = $a; + break; + } + } + /**/ + + $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; + + $d = $lastd; + $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; + for ($a = 13 ; --$a > 0;) + { + $lastd = $d; + $d += $mtab[$a] * $_day_power; + if ($d >= 0) + { + $month = $a; + $ndays = $mtab[$a]; + break; + } + } + + $d = $lastd; + $day = $ndays + ceil(($d+1) / ($_day_power)); + + $d += ($ndays - $day+1)* $_day_power; + $hour = floor($d/$_hour_power); + + } else { + for ($a = 1970 ;; $a++) + { + $lastd = $d; + + if ($leaf = self::isLeapYear($a)) $d -= $d366; + else $d -= $d365; + if ($d < 0) + { + $year = $a; + break; + } + } + $secsInYear = $lastd; + $d = $lastd; + $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; + for ($a = 1 ; $a <= 12; $a++) + { + $lastd = $d; + $d -= $mtab[$a] * $_day_power; + if ($d < 0) + { + $month = $a; + $ndays = $mtab[$a]; + break; + } + } + $d = $lastd; + $day = ceil(($d+1) / $_day_power); + $d = $d - ($day-1) * $_day_power; + $hour = floor($d /$_hour_power); + } + + $d -= $hour * $_hour_power; + $min = floor($d/$_min_power); + $secs = $d - $min * $_min_power; + if ($fast) + { + return array( + 'seconds' => $secs, + 'minutes' => $min, + 'hours' => $hour, + 'mday' => $day, + 'mon' => $month, + 'year' => $year, + 'yday' => floor($secsInYear/$_day_power), + 'leap' => $leaf, + 'ndays' => $ndays + ); + } + + + $dow = self::getDayofWeek($year,$month,$day); + + return array( + 'seconds' => $secs, + 'minutes' => $min, + 'hours' => $hour, + 'mday' => $day, + 'wday' => $dow, + 'mon' => $month, + 'year' => $year, + 'yday' => floor($secsInYear/$_day_power), + 'weekday' => gmdate('l',$_day_power*(3+$dow)), + 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), + 0 => $origd + ); + } + + /** + * Checks to see if the year, month, day are valid combination. + * @param integer year + * @param integer month + * @param integer day + * @return boolean true if valid date, semantic check only. + */ + public static function isValidDate($y,$m,$d) + { + if (self::isLeapYear($y)) + $marr =& self::$_monthLeaf; + else + $marr =& self::$_monthNormal; + + if ($m > 12 || $m < 1) return false; + + if ($d > 31 || $d < 1) return false; + + if ($marr[$m] < $d) return false; + + if ($y < 1000 && $y > 3000) return false; + + return true; + } + + /** + * Checks to see if the hour, minute and second are valid. + * @param integer hour + * @param integer minute + * @param integer second + * @param boolean whether the hours should be 0 through 23 (default) or 1 through 12. + * @return boolean true if valid date, semantic check only. + * @since 1.0.5 + */ + public static function isValidTime($h,$m,$s,$hs24=true) + { + if($hs24 && ($h < 0 || $h > 23) || !$hs24 && ($h < 1 || $h > 12)) return false; + if($m > 59 || $m < 0) return false; + if($s > 59 || $s < 0) return false; + return true; + } + + /** + * Formats a timestamp to a date string. + * @param string format pattern + * @param integer timestamp + * @param boolean whether this is a GMT timestamp + * @return string formatted date based on timestamp $d + */ + public static function formatDate($fmt,$d=false,$is_gmt=false) + { + if ($d === false) + return ($is_gmt)? @gmdate($fmt): @date($fmt); + + // check if number in 32-bit signed range + if ((abs($d) <= 0x7FFFFFFF)) + { + // if windows, must be +ve integer + if ($d >= 0) + return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); + } + + $_day_power = 86400; + + $arr = self::getDate($d,true,$is_gmt); + + $year = $arr['year']; + $month = $arr['mon']; + $day = $arr['mday']; + $hour = $arr['hours']; + $min = $arr['minutes']; + $secs = $arr['seconds']; + + $max = strlen($fmt); + $dates = ''; + + /* + at this point, we have the following integer vars to manipulate: + $year, $month, $day, $hour, $min, $secs + */ + for ($i=0; $i < $max; $i++) + { + switch($fmt[$i]) + { + case 'T': $dates .= date('T');break; + // YEAR + case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; + case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 + + // 4.3.11 uses '04 Jun 2004' + // 4.3.8 uses ' 4 Jun 2004' + $dates .= gmdate('D',$_day_power*(3+self::getDayOfWeek($year,$month,$day))).', ' + . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; + + if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; + + if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; + + if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; + + $gmt = self::getGMTDiff(); + $dates .= sprintf(' %s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); + break; + + case 'Y': $dates .= $year; break; + case 'y': $dates .= substr($year,strlen($year)-2,2); break; + // MONTH + case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; + case 'Q': $dates .= ($month+3)>>2; break; + case 'n': $dates .= $month; break; + case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; + case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; + // DAY + case 't': $dates .= $arr['ndays']; break; + case 'z': $dates .= $arr['yday']; break; + case 'w': $dates .= self::getDayOfWeek($year,$month,$day); break; + case 'l': $dates .= gmdate('l',$_day_power*(3+self::getDayOfWeek($year,$month,$day))); break; + case 'D': $dates .= gmdate('D',$_day_power*(3+self::getDayOfWeek($year,$month,$day))); break; + case 'j': $dates .= $day; break; + case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; + case 'S': + $d10 = $day % 10; + if ($d10 == 1) $dates .= 'st'; + else if ($d10 == 2 && $day != 12) $dates .= 'nd'; + else if ($d10 == 3) $dates .= 'rd'; + else $dates .= 'th'; + break; + + // HOUR + case 'Z': + $dates .= ($is_gmt) ? 0 : -self::getGMTDiff(); break; + case 'O': + $gmt = ($is_gmt) ? 0 : self::getGMTDiff(); + + $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); + break; + + case 'H': + if ($hour < 10) $dates .= '0'.$hour; + else $dates .= $hour; + break; + case 'h': + if ($hour > 12) $hh = $hour - 12; + else { + if ($hour == 0) $hh = '12'; + else $hh = $hour; + } + + if ($hh < 10) $dates .= '0'.$hh; + else $dates .= $hh; + break; + + case 'G': + $dates .= $hour; + break; + + case 'g': + if ($hour > 12) $hh = $hour - 12; + else { + if ($hour == 0) $hh = '12'; + else $hh = $hour; + } + $dates .= $hh; + break; + // MINUTES + case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; + // SECONDS + case 'U': $dates .= $d; break; + case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; + // AM/PM + // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM + case 'a': + if ($hour>=12) $dates .= 'pm'; + else $dates .= 'am'; + break; + case 'A': + if ($hour>=12) $dates .= 'PM'; + else $dates .= 'AM'; + break; + default: + $dates .= $fmt[$i]; break; + // ESCAPE + case "\\": + $i++; + if ($i < $max) $dates .= $fmt[$i]; + break; + } + } + return $dates; + } + + /** + * Generates a timestamp. + * Not a very fast algorithm - O(n) operation. Could be optimized to O(1). + * @param integer hour + * @param integer minute + * @param integer second + * @param integer month + * @param integer day + * @param integer year + * @param boolean whether this is GMT time + * @return integer|float a timestamp given a local time. Originally by jackbbs. + */ + public static function getTimestamp($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_gmt=false) + { + if ($mon === false) + return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec); + + // for windows, we don't check 1970 because with timezone differences, + // 1 Jan 1970 could generate negative timestamp, which is illegal + if (1971 <= $year && $year < 2038) + { + return $is_gmt ? @gmmktime($hr,$min,$sec,$mon,$day,$year) + : @mktime($hr,$min,$sec,$mon,$day,$year); + } + + $gmt_different = ($is_gmt) ? 0 : self::getGMTDiff(); + + /* + # disabled because some people place large values in $sec. + # however we need it for $mon because we use an array... + $hr = intval($hr); + $min = intval($min); + $sec = intval($sec); + */ + $mon = intval($mon); + $day = intval($day); + $year = intval($year); + + + $year = self::digitCheck($year); + + if ($mon > 12) + { + $y = floor($mon / 12); + $year += $y; + $mon -= $y*12; + } + else if ($mon < 1) + { + $y = ceil((1-$mon) / 12); + $year -= $y; + $mon += $y*12; + } + + $_day_power = 86400; + $_hour_power = 3600; + $_min_power = 60; + + $_month_table_normal = & self::$_monthNormal; + $_month_table_leaf = & self::$_monthLeaf; + + $_total_date = 0; + if ($year >= 1970) + { + for ($a = 1970 ; $a <= $year; $a++) + { + $leaf = self::isLeapYear($a); + if ($leaf == true) { + $loop_table = $_month_table_leaf; + $_add_date = 366; + } else { + $loop_table = $_month_table_normal; + $_add_date = 365; + } + if ($a < $year) { + $_total_date += $_add_date; + } else { + for($b=1;$b<$mon;$b++) { + $_total_date += $loop_table[$b]; + } + } + } + $_total_date +=$day-1; + $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; + + } else { + for ($a = 1969 ; $a >= $year; $a--) { + $leaf = self::isLeapYear($a); + if ($leaf == true) { + $loop_table = $_month_table_leaf; + $_add_date = 366; + } else { + $loop_table = $_month_table_normal; + $_add_date = 365; + } + if ($a > $year) { $_total_date += $_add_date; + } else { + for($b=12;$b>$mon;$b--) { + $_total_date += $loop_table[$b]; + } + } + } + $_total_date += $loop_table[$mon] - $day; + + $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; + $_day_time = $_day_power - $_day_time; + $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); + if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction + else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. + } + + return $ret; + } +} diff --git a/web/framework-1.0.8/utils/CVarDumper.php b/web/framework-1.0.8/utils/CVarDumper.php new file mode 100644 index 0000000..1b0da5b --- /dev/null +++ b/web/framework-1.0.8/utils/CVarDumper.php @@ -0,0 +1,142 @@ +<?php +/** + * CVarDumper class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CVarDumper is intended to replace the buggy PHP function var_dump and print_r. + * It can correctly identify the recursively referenced objects in a complex + * object structure. It also has a recursive depth control to avoid indefinite + * recursive display of some peculiar variables. + * + * CVarDumper can be used as follows, + * <pre> + * CVarDumper::dump($var); + * </pre> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CVarDumper.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.utils + * @since 1.0 + */ +class CVarDumper +{ + private static $_objects; + private static $_output; + private static $_depth; + + /** + * Displays a variable. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as Yii controllers. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @param boolean whether the result should be syntax-highlighted + */ + public static function dump($var,$depth=10,$highlight=false) + { + echo self::dumpAsString($var,$depth,$highlight); + } + + public static function dumperror($var,$depth=10) + { + debuglog(self::dumpAsString($var,$depth)."\r\n", 3, YAAMP_LOGS."/debug.log"); + } + + /** + * Dumps a variable in terms of a string. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as Yii controllers. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @param boolean whether the result should be syntax-highlighted + * @return string the string representation of the variable + */ + public static function dumpAsString($var,$depth=10,$highlight=false) + { + // + self::$_output=''; + self::$_objects=array(); + self::$_depth=$depth; + self::dumpInternal($var,0); + if($highlight) + { + $result=highlight_string("<?php\r\n".self::$_output,true); + self::$_output=preg_replace('/<\\?php<br \\/>/','',$result,1); + } + return self::$_output; + } + + private static function dumpInternal($var,$level) + { + switch(gettype($var)) + { + case 'boolean': + self::$_output.=$var?'true':'false'; + break; + case 'integer': + self::$_output.="$var"; + break; + case 'double': + self::$_output.="$var"; + break; + case 'string': + self::$_output.="'$var'"; + break; + case 'resource': + self::$_output.='{resource}'; + break; + case 'NULL': + self::$_output.="null"; + break; + case 'unknown type': + self::$_output.='{unknown}'; + break; + case 'array': + if(self::$_depth<=$level) + self::$_output.='array(...)'; + else if(empty($var)) + self::$_output.='array()'; + else + { + $keys=array_keys($var); + $spaces=str_repeat(' ',$level*4); + self::$_output.="array\n".$spaces.'('; + foreach($keys as $key) + { + self::$_output.="\n".$spaces." [$key] => "; + self::$_output.=self::dumpInternal($var[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + case 'object': + if(($id=array_search($var,self::$_objects,true))!==false) + self::$_output.=get_class($var).'#'.($id+1).'(...)'; + else if(self::$_depth<=$level) + self::$_output.=get_class($var).'(...)'; + else + { + $id=array_push(self::$_objects,$var); + $className=get_class($var); + $members=(array)$var; + $keys=array_keys($members); + $spaces=str_repeat(' ',$level*4); + self::$_output.="$className#$id\n".$spaces.'('; + foreach($keys as $key) + { + $keyDisplay=strtr(trim($key),array("\0"=>':')); + self::$_output.="\n".$spaces." [$keyDisplay] => "; + self::$_output.=self::dumpInternal($members[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + } + } +} diff --git a/web/framework-1.0.8/utils/mimeTypes.php b/web/framework-1.0.8/utils/mimeTypes.php new file mode 100644 index 0000000..7766b5b --- /dev/null +++ b/web/framework-1.0.8/utils/mimeTypes.php @@ -0,0 +1,178 @@ +<?php +/** + * MIME types. + * + * This file contains most commonly used MIME types + * according to file extension names. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: mimeTypes.php 1023 2009-05-11 17:49:09Z qiang.xue $ + */ + +return array( + 'ai'=>'application/postscript', + 'aif'=>'audio/x-aiff', + 'aifc'=>'audio/x-aiff', + 'aiff'=>'audio/x-aiff', + 'asc'=>'text/plain', + 'au'=>'audio/basic', + 'avi'=>'video/x-msvideo', + 'bcpio'=>'application/x-bcpio', + 'bin'=>'application/octet-stream', + 'bmp'=>'image/bmp', + 'c'=>'text/plain', + 'cc'=>'text/plain', + 'ccad'=>'application/clariscad', + 'cdf'=>'application/x-netcdf', + 'class'=>'application/octet-stream', + 'cpio'=>'application/x-cpio', + 'cpt'=>'application/mac-compactpro', + 'csh'=>'application/x-csh', + 'css'=>'text/css', + 'dcr'=>'application/x-director', + 'dir'=>'application/x-director', + 'dms'=>'application/octet-stream', + 'doc'=>'application/msword', + 'drw'=>'application/drafting', + 'dvi'=>'application/x-dvi', + 'dwg'=>'application/acad', + 'dxf'=>'application/dxf', + 'dxr'=>'application/x-director', + 'eps'=>'application/postscript', + 'etx'=>'text/x-setext', + 'exe'=>'application/octet-stream', + 'ez'=>'application/andrew-inset', + 'f'=>'text/plain', + 'f90'=>'text/plain', + 'fli'=>'video/x-fli', + 'flv'=>'video/x-flv', + 'gif'=>'image/gif', + 'gtar'=>'application/x-gtar', + 'gz'=>'application/x-gzip', + 'h'=>'text/plain', + 'hdf'=>'application/x-hdf', + 'hh'=>'text/plain', + 'hqx'=>'application/mac-binhex40', + 'htm'=>'text/html', + 'html'=>'text/html', + 'ice'=>'x-conference/x-cooltalk', + 'ief'=>'image/ief', + 'iges'=>'model/iges', + 'igs'=>'model/iges', + 'ips'=>'application/x-ipscript', + 'ipx'=>'application/x-ipix', + 'jpe'=>'image/jpeg', + 'jpeg'=>'image/jpeg', + 'jpg'=>'image/jpeg', + 'js'=>'application/x-javascript', + 'kar'=>'audio/midi', + 'latex'=>'application/x-latex', + 'lha'=>'application/octet-stream', + 'lsp'=>'application/x-lisp', + 'lzh'=>'application/octet-stream', + 'm'=>'text/plain', + 'man'=>'application/x-troff-man', + 'me'=>'application/x-troff-me', + 'mesh'=>'model/mesh', + 'mid'=>'audio/midi', + 'midi'=>'audio/midi', + 'mif'=>'application/vnd.mif', + 'mime'=>'www/mime', + 'mov'=>'video/quicktime', + 'movie'=>'video/x-sgi-movie', + 'mp2'=>'audio/mpeg', + 'mp3'=>'audio/mpeg', + 'mpe'=>'video/mpeg', + 'mpeg'=>'video/mpeg', + 'mpg'=>'video/mpeg', + 'mpga'=>'audio/mpeg', + 'ms'=>'application/x-troff-ms', + 'msh'=>'model/mesh', + 'nc'=>'application/x-netcdf', + 'oda'=>'application/oda', + 'pbm'=>'image/x-portable-bitmap', + 'pdb'=>'chemical/x-pdb', + 'pdf'=>'application/pdf', + 'pgm'=>'image/x-portable-graymap', + 'pgn'=>'application/x-chess-pgn', + 'png'=>'image/png', + 'pnm'=>'image/x-portable-anymap', + 'pot'=>'application/mspowerpoint', + 'ppm'=>'image/x-portable-pixmap', + 'pps'=>'application/mspowerpoint', + 'ppt'=>'application/mspowerpoint', + 'ppz'=>'application/mspowerpoint', + 'pre'=>'application/x-freelance', + 'prt'=>'application/pro_eng', + 'ps'=>'application/postscript', + 'qt'=>'video/quicktime', + 'ra'=>'audio/x-realaudio', + 'ram'=>'audio/x-pn-realaudio', + 'ras'=>'image/cmu-raster', + 'rgb'=>'image/x-rgb', + 'rm'=>'audio/x-pn-realaudio', + 'roff'=>'application/x-troff', + 'rpm'=>'audio/x-pn-realaudio-plugin', + 'rtf'=>'text/rtf', + 'rtx'=>'text/richtext', + 'scm'=>'application/x-lotusscreencam', + 'set'=>'application/set', + 'sgm'=>'text/sgml', + 'sgml'=>'text/sgml', + 'sh'=>'application/x-sh', + 'shar'=>'application/x-shar', + 'silo'=>'model/mesh', + 'sit'=>'application/x-stuffit', + 'skd'=>'application/x-koan', + 'skm'=>'application/x-koan', + 'skp'=>'application/x-koan', + 'skt'=>'application/x-koan', + 'smi'=>'application/smil', + 'smil'=>'application/smil', + 'snd'=>'audio/basic', + 'sol'=>'application/solids', + 'spl'=>'application/x-futuresplash', + 'src'=>'application/x-wais-source', + 'step'=>'application/STEP', + 'stl'=>'application/SLA', + 'stp'=>'application/STEP', + 'sv4cpio'=>'application/x-sv4cpio', + 'sv4crc'=>'application/x-sv4crc', + 'swf'=>'application/x-shockwave-flash', + 't'=>'application/x-troff', + 'tar'=>'application/x-tar', + 'tcl'=>'application/x-tcl', + 'tex'=>'application/x-tex', + 'texi'=>'application/x-texinfo', + 'texinfo'=>'application/x-texinfo', + 'tif'=>'image/tiff', + 'tiff'=>'image/tiff', + 'tr'=>'application/x-troff', + 'tsi'=>'audio/TSP-audio', + 'tsp'=>'application/dsptype', + 'tsv'=>'text/tab-separated-values', + 'txt'=>'text/plain', + 'unv'=>'application/i-deas', + 'ustar'=>'application/x-ustar', + 'vcd'=>'application/x-cdlink', + 'vda'=>'application/vda', + 'viv'=>'video/vnd.vivo', + 'vivo'=>'video/vnd.vivo', + 'vrml'=>'model/vrml', + 'wav'=>'audio/x-wav', + 'wrl'=>'model/vrml', + 'xbm'=>'image/x-xbitmap', + 'xlc'=>'application/vnd.ms-excel', + 'xll'=>'application/vnd.ms-excel', + 'xlm'=>'application/vnd.ms-excel', + 'xls'=>'application/vnd.ms-excel', + 'xlw'=>'application/vnd.ms-excel', + 'xml'=>'text/xml', + 'xpm'=>'image/x-xpixmap', + 'xwd'=>'image/x-xwindowdump', + 'xyz'=>'chemical/x-pdb', + 'zip'=>'application/zip', +); diff --git a/web/framework-1.0.8/validators/CCaptchaValidator.php b/web/framework-1.0.8/validators/CCaptchaValidator.php new file mode 100644 index 0000000..4a3c854 --- /dev/null +++ b/web/framework-1.0.8/validators/CCaptchaValidator.php @@ -0,0 +1,72 @@ +<?php +/** + * CCaptchaValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCaptchaValidator validates that the attribute value is the same as the verification code displayed in the CAPTCHA. + * + * CCaptchaValidator should be used together with {@link CCaptchaAction}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CCaptchaValidator.php 910 2009-04-02 12:57:06Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CCaptchaValidator extends CValidator +{ + /** + * @var boolean whether the comparison is case sensitive. Defaults to false. + */ + public $caseSensitive=false; + /** + * @var string ID of the action that renders the CAPTCHA image. Defaults to 'captcha', + * meaning the 'captcha' action declared in the current controller. + * This can also be a route consisting of controller ID and action ID. + */ + public $captchaAction='captcha'; + /** + * @var boolean whether the attribute value can be null or empty. + * Defaults to false, meaning the attribute is invalid if it is empty. + */ + public $allowEmpty=false; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + + if(($captcha=Yii::app()->getController()->createAction($this->captchaAction))===null) + { + if(strpos($this->captchaAction,'/')!==false) // contains controller or module + { + if(($ca=Yii::app()->createController($this->captchaAction))!==null) + { + list($controller,$actionID)=$ca; + $captcha=$controller->createAction($actionID); + } + } + if($captcha===null) + throw new CException(Yii::t('yii','CCaptchaValidator.action "{id}" is invalid. Unable to find such an action in the current controller.', + array('{id}'=>$this->captchaAction))); + } + if(!$captcha->validate($value,$this->caseSensitive)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','The verification code is incorrect.'); + $this->addError($object,$attribute,$message); + } + } +} + diff --git a/web/framework-1.0.8/validators/CCompareValidator.php b/web/framework-1.0.8/validators/CCompareValidator.php new file mode 100644 index 0000000..5d53667 --- /dev/null +++ b/web/framework-1.0.8/validators/CCompareValidator.php @@ -0,0 +1,151 @@ +<?php +/** + * CCompareValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCompareValidator compares the specified attribute value with another value and validates if they are equal. + * + * The value being compared with can be another attribute value + * (specified via {@link compareAttribute}) or a constant (specified via + * {@link compareValue}. When both are specified, the latter takes + * precedence. If neither is specified, the attribute will be compared + * with another attribute whose name is by appending "_repeat" to the source + * attribute name. + * + * The comparison can be either {@link strict} or not. + * + * Starting from version 1.0.8, CCompareValidator supports different comparison operators. + * Previously, it only compares to see if two values are equal or not. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CCompareValidator.php 1307 2009-08-07 17:50:52Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CCompareValidator extends CValidator +{ + /** + * @var string the name of the attribute to be compared with + */ + public $compareAttribute; + /** + * @var string the constant value to be compared with + */ + public $compareValue; + /** + * @var boolean whether the comparison is strict (both value and type must be the same.) + * Defaults to false. + */ + public $strict=false; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + /** + * @var string the operator for comparison. Defaults to '='. + * The followings are valid operators: + * <ul> + * <li>'=' or '==': validates to see if the two values are equal. If {@link strict} is true, the comparison + * will be done in strict mode (i.e. checking value type as well).</li> + * <li>'!=': validates to see if the two values are NOT equal. If {@link strict} is true, the comparison + * will be done in strict mode (i.e. checking value type as well).</li> + * <li>'>': validates to see if the value being validated is greater than the value being compared with.</li> + * <li>'>=': validates to see if the value being validated is greater than or equal to the value being compared with.</li> + * <li>'<': validates to see if the value being validated is less than the value being compared with.</li> + * <li>'<=': validates to see if the value being validated is less than or equal to the value being compared with.</li> + * </ul> + * @since 1.0.8 + */ + public $operator='='; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + if($this->compareValue!==null) + $compareTo=$compareValue=$this->compareValue; + else + { + $compareAttribute=$this->compareAttribute===null ? $attribute.'_repeat' : $this->compareAttribute; + $compareValue=$object->$compareAttribute; + $compareTo=$object->getAttributeLabel($compareAttribute); + } + + switch($this->operator) + { + case '=': + case '==': + if(($this->strict && $value!==$compareValue) || (!$this->strict && $value!=$compareValue)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be repeated exactly.'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo)); + } + break; + case '!=': + if(($this->strict && $value===$compareValue) || (!$this->strict && $value==$compareValue)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must not be equal to "{compareValue}".'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo,'{compareValue}'=>$compareValue)); + } + break; + case '>': + if($value<=$compareValue) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be greater than "{compareValue}".'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo,'{compareValue}'=>$compareValue)); + } + break; + case '>=': + if($value<$compareValue) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be greater than or equal to "{compareValue}".'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo,'{compareValue}'=>$compareValue)); + } + break; + case '<': + if($value>=$compareValue) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be less than "{compareValue}".'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo,'{compareValue}'=>$compareValue)); + } + break; + case '<=': + if($value>$compareValue) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be less than or equal to "{compareValue}".'); + $this->addError($object,$attribute,$message,array('{compareAttribute}'=>$compareTo,'{compareValue}'=>$compareValue)); + } + break; + default: + throw new CException(Yii::t('yii','Invalid operator "{operator}".',array('{operator}'=>$this->operator))); + } + } + + protected function compareValues($value,$value2) + { + switch($this->operator) + { + case '=': + case '==': + return ($this->strict && $value===$compareValue) || (!$this->strict && $value==$compareValue); + case '!=': + return ($this->strict && $value!==$compareValue) || (!$this->strict && $value!=$compareValue); + case '>': + return $value>$compareValue; + } + } +} diff --git a/web/framework-1.0.8/validators/CDefaultValueValidator.php b/web/framework-1.0.8/validators/CDefaultValueValidator.php new file mode 100644 index 0000000..80280f0 --- /dev/null +++ b/web/framework-1.0.8/validators/CDefaultValueValidator.php @@ -0,0 +1,51 @@ +<?php +/** + * CDefaultValueValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDefaultValueValidator sets the attributes with the specified value. + * It does not do validation. Its existence is mainly to allow + * specifying attribute default values in a dynamic way. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDefaultValueValidator.php 547 2009-01-19 19:00:35Z qiang.xue $ + * @package system.validators + * @since 1.0.2 + */ +class CDefaultValueValidator extends CValidator +{ + /** + * @var mixed the default value to be set to the specified attributes. + */ + public $value; + /** + * @var boolean whether to set the default value only when the attribute value is null or empty string. + * Defaults to true. If false, the attribute will always be assigned with the default value, + * even if it is already explicitly assigned a value. + */ + public $setOnEmpty=true; + + /** + * Validates the attribute of the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + if(!$this->setOnEmpty) + $object->$attribute=$this->value; + else + { + $value=$object->$attribute; + if($value===null || $value==='') + $object->$attribute=$this->value; + } + } +} + diff --git a/web/framework-1.0.8/validators/CEmailValidator.php b/web/framework-1.0.8/validators/CEmailValidator.php new file mode 100644 index 0000000..79ee5a6 --- /dev/null +++ b/web/framework-1.0.8/validators/CEmailValidator.php @@ -0,0 +1,80 @@ +<?php +/** + * CEmailValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CEmailValidator validates that the attribute value is a valid email address. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CEmailValidator.php 986 2009-05-07 16:47:27Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CEmailValidator extends CValidator +{ + /** + * @var string the regular expression used to validate the attribute value. + */ + public $pattern='/^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$/'; + /** + * @var string the regular expression used to validate email addresses with the name part. + * This property is used only when {@link allowName} is true. + * @since 1.0.5 + * @see allowName + */ + public $fullPattern='/^[^@]*<\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*>$/'; + /** + * @var boolean whether to allow name in the email address (e.g. "Qiang Xue <qiang.xue@gmail.com>"). Defaults to false. + * @since 1.0.5 + * @see fullPattern + */ + public $allowName=false; + /** + * @var boolean whether to check the MX record for the email address. + * Defaults to false. To enable it, you need to make sure the PHP function 'checkdnsrr' + * exists in your PHP installation. + */ + public $checkMX=false; + /** + * @var boolean whether to check port 25 for the email address. + * Defaults to false. + * @since 1.0.4 + */ + public $checkPort=false; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + $valid=preg_match($this->pattern,$value) || $this->allowName && preg_match($this->fullPattern,$value); + if($valid) + $domain=rtrim(substr($value,strpos($value,'@')+1),'>'); + if($valid && $this->checkMX && function_exists('checkdnsrr')) + $valid=checkdnsrr($domain,'MX'); + if($valid && $this->checkPort && function_exists('fsockopen')) + $valid=fsockopen($domain,25)!==false; + if(!$valid) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is not a valid email address.'); + $this->addError($object,$attribute,$message); + } + } +} diff --git a/web/framework-1.0.8/validators/CExistValidator.php b/web/framework-1.0.8/validators/CExistValidator.php new file mode 100644 index 0000000..3ec670f --- /dev/null +++ b/web/framework-1.0.8/validators/CExistValidator.php @@ -0,0 +1,88 @@ +<?php +/** + * CExistValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CExistValidator validates that the attribute value exists in a table. + * + * This validator is often used to verify that a foreign key contains a value + * that can be found in the foreign table. + * + * CExistValidator can only be used for active record objects. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CExistValidator.php 1268 2009-07-22 15:37:50Z qiang.xue $ + * @package system.validators + * @since 1.0.4 + */ +class CExistValidator extends CValidator +{ + /** + * @var string the ActiveRecord class name that should be used to + * look for the attribute value being validated. Defaults to null, + * meaning using the ActiveRecord class of the attribute being validated. + * You may use path alias to reference a class name here. + * @see attributeName + */ + public $className; + /** + * @var string the ActiveRecord class attribute name that should be + * used to look for the attribute value being validated. Defaults to null, + * meaning using the name of the attribute being validated. + * @see className + */ + public $attributeName; + /** + * @var array additional query criteria. This will be combined with the condition + * that checks if the attribute value exists in the corresponding table column. + * This array will be used to instantiate a {@link CDbCriteria} object. + * @since 1.0.8 + */ + public $criteria=array(); + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + + $className=$this->className===null?get_class($object):Yii::import($this->className); + $attributeName=$this->attributeName===null?$attribute:$this->attributeName; + $finder=CActiveRecord::model($className); + $table=$finder->getTableSchema(); + if(($column=$table->getColumn($attributeName))===null) + throw new CException(Yii::t('yii','Column "{column} does not exist in table "{table}".', + array('{column}'=>$attributeName,'{table}'=>$table->name))); + + $criteria=array('condition'=>$column->rawName.'=:vp','params'=>array(':vp'=>$value)); + if($this->criteria!==array()) + { + $criteria=new CDbCriteria($criteria); + $criteria->mergeWith($this->criteria); + } + + if(!$finder->exists($criteria)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} "{value}" is invalid.'); + $this->addError($object,$attribute,$message,array('{value}'=>$value)); + } + } +} + diff --git a/web/framework-1.0.8/validators/CFileValidator.php b/web/framework-1.0.8/validators/CFileValidator.php new file mode 100644 index 0000000..55fdfc8 --- /dev/null +++ b/web/framework-1.0.8/validators/CFileValidator.php @@ -0,0 +1,159 @@ +<?php +/** + * CFileValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileValidator verifies if an attribute is receiving a valid uploaded file. + * + * It uses the model class and attribute name to retrieve the information + * about the uploaded file. It then checks if a file is uploaded successfully, + * if the file size is within the limit and if the file type is allowed. + * + * When using CFileValidator with an active record, the following code is often used: + * <pre> + * // assuming the upload file field is generated using + * // CHtml::activeFileField($model,'file'); + * $model->file=CUploadedFile::getInstance($model,'file'); + * $model->fileSize=$file->size; + * if($model->save()) + * $model->file->saveAs($path); // save the uploaded file + * </pre> + * + * You can use {@link CFileValidator} to validate the file attribute. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileValidator.php 1271 2009-07-22 20:25:46Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CFileValidator extends CValidator +{ + /** + * @var boolean whether the attribute requires a file to be uploaded or not. + * Defaults to false, meaning a file is required to be uploaded. + */ + public $allowEmpty=false; + /** + * @var mixed a list of file name extensions that are allowed to be uploaded. + * This can be either an array or a string consisting of file extension names + * separated by space or comma (e.g. "gif, jpg"). + * Extension names are case-insensitive. Defaults to null, meaning all file name + * extensions are allowed. + */ + public $types; + /** + * @var integer the minimum number of bytes required for the uploaded file. + * Defaults to null, meaning no limit. + * @see tooSmall + */ + public $minSize; + /** + * @var integer the maximum number of bytes required for the uploaded file. + * Defaults to null, meaning no limit. + * Note, the size limit is also affected by 'upload_max_filesize' INI setting + * and the 'MAX_FILE_SIZE' hidden field value. + * @see tooLarge + */ + public $maxSize; + /** + * @var string the error message used when the uploaded file is too large. + * @see maxSize + */ + public $tooLarge; + /** + * @var string the error message used when the uploaded file is too small. + * @see minSize + */ + public $tooSmall; + /** + * @var string the error message used when the uploaded file has an extension name + * that is not listed among {@link extensions}. + */ + public $wrongType; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $file=$object->$attribute; + if(!($file instanceof CUploadedFile)) + $file=CUploadedFile::getInstance($object,$attribute); + + if($this->allowEmpty && ($file===null || $file->getError()==UPLOAD_ERR_NO_FILE)) + return; + + if($file===null || $file->getError()==UPLOAD_ERR_NO_FILE) + { + $message=$this->message!==null?$this->message : Yii::t('yii','{attribute} cannot be blank.'); + $this->addError($object,$attribute,$message); + return; + } + + $error=$file->getError(); + if($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize) + { + $message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file "{file}" is too large. Its size cannot exceed {limit} bytes.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->getSizeLimit())); + } + else if($error==UPLOAD_ERR_PARTIAL) + throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>$file->getName()))); + else if($error==UPLOAD_ERR_NO_TMP_DIR) + throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>$file->getName()))); + else if($error==UPLOAD_ERR_CANT_WRITE) + throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>$file->getName()))); + else if(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION) // available for PHP 5.2.0 or above + throw new CException(Yii::t('yii','File upload was stopped by extension.')); + + if($this->minSize!==null && $file->getSize()<$this->minSize) + { + $message=$this->tooSmall!==null?$this->tooSmall : Yii::t('yii','The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->minSize)); + } + + if($this->types!==null) + { + if(is_string($this->types)) + $types=preg_split('/[\s,]+/',strtolower($this->types),-1,PREG_SPLIT_NO_EMPTY); + else + $types=$this->types; + if(!in_array(strtolower($file->getExtensionName()),$types)) + { + $message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types))); + } + } + } + + /** + * Returns the maximum size allowed for uploaded files. + * This is determined based on three factors: + * <ul> + * <li>'upload_max_filesize' in php.ini</li> + * <li>'MAX_FILE_SIZE' hidden field</li> + * <li>{@link maxSize}</li> + * </ul> + * + * @return integer the size limit for uploaded files. + */ + protected function getSizeLimit() + { + $limit=ini_get('upload_max_filesize'); + if(strpos($limit,'M')!==false) + $limit=$limit*1024*1024; + if($this->maxSize!==null && $limit>0 && $this->maxSize<$limit) + $limit=$this->maxSize; + if(isset($_POST['MAX_FILE_SIZE']) && $_POST['MAX_FILE_SIZE']>0 && $_POST['MAX_FILE_SIZE']<$limit) + $limit=$_POST['MAX_FILE_SIZE']; + return $limit; + } +} diff --git a/web/framework-1.0.8/validators/CFilterValidator.php b/web/framework-1.0.8/validators/CFilterValidator.php new file mode 100644 index 0000000..50d431a --- /dev/null +++ b/web/framework-1.0.8/validators/CFilterValidator.php @@ -0,0 +1,49 @@ +<?php +/** + * CFilterValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFilterValidator transforms the data being validated based on a filter. + * + * CFilterValidator is actually not a validator but a data processor. + * It invokes the specified filter method to process the attribute value + * and save the processed value back to the attribute. The filter method + * must follow the following signature: + * <pre> + * function foo($value) {...return $newValue; } + * </pre> + * Many PHP functions qualify this signature (e.g. trim). + * + * To specify the filter method, set {@link filter} property to be the function name. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFilterValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CFilterValidator extends CValidator +{ + /** + * @var callback the filter method + */ + public $filter; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + if($this->filter===null || !is_callable($this->filter)) + throw new CException(Yii::t('yii','The "filter" property must be specified with a valid callback.')); + $object->$attribute=call_user_func_array($this->filter,$object->$attribute); + } +} diff --git a/web/framework-1.0.8/validators/CInlineValidator.php b/web/framework-1.0.8/validators/CInlineValidator.php new file mode 100644 index 0000000..c3e9590 --- /dev/null +++ b/web/framework-1.0.8/validators/CInlineValidator.php @@ -0,0 +1,41 @@ +<?php +/** + * CInlineValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CInlineValidator represents a validator which is defined as a method in the object being validated. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CInlineValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CInlineValidator extends CValidator +{ + /** + * @var string the name of the validation method defined in the active record class + */ + public $method; + /** + * @var array additional parameters that are passed to the validation method + */ + public $params; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $method=$this->method; + $object->$method($attribute,$this->params); + } +} diff --git a/web/framework-1.0.8/validators/CNumberValidator.php b/web/framework-1.0.8/validators/CNumberValidator.php new file mode 100644 index 0000000..b2c9eec --- /dev/null +++ b/web/framework-1.0.8/validators/CNumberValidator.php @@ -0,0 +1,86 @@ +<?php +/** + * CNumberValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CNumberValidator validates that the attribute value is a number. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CNumberValidator.php 1178 2009-06-26 20:13:31Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CNumberValidator extends CValidator +{ + /** + * @var boolean whether the attribute value can only be an integer. Defaults to false. + */ + public $integerOnly=false; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + /** + * @var integer|double upper limit of the number. Defaults to null, meaning no upper limit. + */ + public $max; + /** + * @var integer|double lower limit of the number. Defaults to null, meaning no lower limit. + */ + public $min; + /** + * @var string user-defined error message used when the value is too big. + */ + public $tooBig; + /** + * @var string user-defined error message used when the value is too small. + */ + public $tooSmall; + + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + if($this->integerOnly) + { + if(is_string($value) && !preg_match('/^\s*[+-]?\d+\s*$/',$value)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be an integer.'); + $this->addError($object,$attribute,$message); + } + } + else + { + if(is_string($value) && !preg_match('/^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$/',$value)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} must be a number.'); + $this->addError($object,$attribute,$message); + } + } + if($this->min!==null && $value<$this->min) + { + $message=$this->tooSmall!==null?$this->tooSmall:Yii::t('yii','{attribute} is too small (minimum is {min}).'); + $this->addError($object,$attribute,$message,array('{min}'=>$this->min)); + } + if($this->max!==null && $value>$this->max) + { + $message=$this->tooBig!==null?$this->tooBig:Yii::t('yii','{attribute} is too big (maximum is {max}).'); + $this->addError($object,$attribute,$message,array('{max}'=>$this->max)); + } + } +} diff --git a/web/framework-1.0.8/validators/CRangeValidator.php b/web/framework-1.0.8/validators/CRangeValidator.php new file mode 100644 index 0000000..54f8f83 --- /dev/null +++ b/web/framework-1.0.8/validators/CRangeValidator.php @@ -0,0 +1,53 @@ +<?php +/** + * CRangeValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CRangeValidator validates that the attribute value is among the list (specified via {@link range}). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CRangeValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CRangeValidator extends CValidator +{ + /** + * @var array list of valid values that the attribute value should be among + */ + public $range; + /** + * @var boolean whether the comparison is strict (both type and value must be the same) + */ + public $strict=false; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + if(is_array($this->range) && !in_array($value,$this->range,$this->strict)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is not in the list.'); + $this->addError($object,$attribute,$message); + } + } +} + diff --git a/web/framework-1.0.8/validators/CRegularExpressionValidator.php b/web/framework-1.0.8/validators/CRegularExpressionValidator.php new file mode 100644 index 0000000..effe9c1 --- /dev/null +++ b/web/framework-1.0.8/validators/CRegularExpressionValidator.php @@ -0,0 +1,51 @@ +<?php +/** + * CRegularExpressionValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CRegularExpressionValidator validates that the attribute value matches to the specified {@link pattern regular expression}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CRegularExpressionValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CRegularExpressionValidator extends CValidator +{ + /** + * @var string the regular expression to be matched with + */ + public $pattern; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + if($this->pattern===null) + throw new CException(Yii::t('yii','The "pattern" property must be specified with a valid regular expression.')); + if(!preg_match($this->pattern,$value)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is invalid.'); + $this->addError($object,$attribute,$message); + } + } +} + diff --git a/web/framework-1.0.8/validators/CRequiredValidator.php b/web/framework-1.0.8/validators/CRequiredValidator.php new file mode 100644 index 0000000..a668c88 --- /dev/null +++ b/web/framework-1.0.8/validators/CRequiredValidator.php @@ -0,0 +1,36 @@ +<?php +/** + * CRequiredValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CRequiredValidator validates that the specified attribute does not have null or empty value. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CRequiredValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CRequiredValidator extends CValidator +{ + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($value===null || trim($value)==='') + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} cannot be blank.'); + $this->addError($object,$attribute,$message); + } + } +} diff --git a/web/framework-1.0.8/validators/CStringValidator.php b/web/framework-1.0.8/validators/CStringValidator.php new file mode 100644 index 0000000..39e7024 --- /dev/null +++ b/web/framework-1.0.8/validators/CStringValidator.php @@ -0,0 +1,78 @@ +<?php +/** + * CStringValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CStringValidator validates that the attribute value is of certain length. + * + * Note, this validator should only be used with string-typed attributes. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CStringValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CStringValidator extends CValidator +{ + /** + * @var integer maximum length. Defaults to null, meaning no maximum limit. + */ + public $max; + /** + * @var integer minimum length. Defaults to null, meaning no minimum limit. + */ + public $min; + /** + * @var integer exact length. Defaults to null, meaning no exact length limit. + */ + public $is; + /** + * @var string user-defined error message used when the value is too long. + */ + public $tooShort; + /** + * @var string user-defined error message used when the value is too short. + */ + public $tooLong; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + $length=strlen($value); + if($this->min!==null && $length<$this->min) + { + $message=$this->tooShort!==null?$this->tooShort:Yii::t('yii','{attribute} is too short (minimum is {min} characters).'); + $this->addError($object,$attribute,$message,array('{min}'=>$this->min)); + } + if($this->max!==null && $length>$this->max) + { + $message=$this->tooLong!==null?$this->tooLong:Yii::t('yii','{attribute} is too long (maximum is {max} characters).'); + $this->addError($object,$attribute,$message,array('{max}'=>$this->max)); + } + if($this->is!==null && $length!==$this->is) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is of the wrong length (should be {length} characters).'); + $this->addError($object,$attribute,$message,array('{length}'=>$this->is)); + } + } +} + diff --git a/web/framework-1.0.8/validators/CTypeValidator.php b/web/framework-1.0.8/validators/CTypeValidator.php new file mode 100644 index 0000000..7bd6b2c --- /dev/null +++ b/web/framework-1.0.8/validators/CTypeValidator.php @@ -0,0 +1,99 @@ +<?php +/** + * CTypeValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTypeValidator verifies if the attribute is of the type specified by {@link type}. + * + * The following data types are supported: + * <ul> + * <li><b>integer</b> A 32-bit signed integer data type.</li> + * <li><b>float</b> A double-precision floating point number data type.</li> + * <li><b>string</b> A string data type.</li> + * <li><b>date</b> A date data type.</li> + * <li><b>time</b> A time data type (available since version 1.0.5).</li> + * <li><b>datetime</b> A date and time data type (available since version 1.0.5).</li> + * </ul> + * + * For <b>date</b> type, the property {@link dateFormat} + * will be used to determine how to parse the date string. If the given date + * value doesn't follow the format, the attribute is considered as invalid. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CTypeValidator.php 976 2009-05-05 20:26:12Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CTypeValidator extends CValidator +{ + /** + * @var string the data type that the attribute should be. Defaults to 'string'. + * Valid values include 'string', 'integer', 'float', 'date', 'time' and 'datetime'. + * Note that 'time' and 'datetime' have been available since version 1.0.5. + */ + public $type='string'; + /** + * @var string the format pattern that the date value should follow. Defaults to 'MM/dd/yyyy'. + * Please see {@link CDateTimeParser} for details about how to specify a date format. + * This property is effective only when {@link type} is 'date'. + */ + public $dateFormat='MM/dd/yyyy'; + /** + * @var string the format pattern that the time value should follow. Defaults to 'hh:mm'. + * Please see {@link CDateTimeParser} for details about how to specify a time format. + * This property is effective only when {@link type} is 'time'. + * @since 1.0.5 + */ + public $timeFormat='hh:mm'; + /** + * @var string the format pattern that the datetime value should follow. Defaults to 'MM/dd/yyyy hh:mm'. + * Please see {@link CDateTimeParser} for details about how to specify a datetime format. + * This property is effective only when {@link type} is 'datetime'. + * @since 1.0.5 + */ + public $datetimeFormat='MM/dd/yyyy hh:mm'; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + + if($this->type==='integer') + $valid=preg_match('/^[-+]?[0-9]+$/',trim($value)); + else if($this->type==='float') + $valid=preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value)); + else if($this->type==='date') + $valid=CDateTimeParser::parse($value,$this->dateFormat)!==false; + else if($this->type==='time') + $valid=CDateTimeParser::parse($value,$this->timeFormat)!==false; + else if($this->type==='datetime') + $valid=CDateTimeParser::parse($value,$this->datetimeFormat)!==false; + else + return; + + if(!$valid) + { + $message=$this->message!==null?$this->message : Yii::t('yii','{attribute} must be {type}.'); + $this->addError($object,$attribute,$message,array('{type}'=>$this->type)); + } + } +} + diff --git a/web/framework-1.0.8/validators/CUniqueValidator.php b/web/framework-1.0.8/validators/CUniqueValidator.php new file mode 100644 index 0000000..155ede4 --- /dev/null +++ b/web/framework-1.0.8/validators/CUniqueValidator.php @@ -0,0 +1,106 @@ +<?php +/** + * CUniqueValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUniqueValidator validates that the attribute value is unique in the corresponding database table. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CUniqueValidator.php 1298 2009-08-07 00:01:24Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CUniqueValidator extends CValidator +{ + /** + * @var boolean whether the comparison is case sensitive. Defaults to true. + * Note, by setting it to false, you are assuming the attribute type is string. + */ + public $caseSensitive=true; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + /** + * @var string the ActiveRecord class name that should be used to + * look for the attribute value being validated. Defaults to null, meaning using + * the class of the object currently being validated. + * You may use path alias to reference a class name here. + * @see attributeName + * @since 1.0.8 + */ + public $className; + /** + * @var string the ActiveRecord class attribute name that should be + * used to look for the attribute value being validated. Defaults to null, + * meaning using the name of the attribute being validated. + * @see className + * @since 1.0.8 + */ + public $attributeName; + /** + * @var array additional query criteria. This will be combined with the condition + * that checks if the attribute value exists in the corresponding table column. + * This array will be used to instantiate a {@link CDbCriteria} object. + * @since 1.0.8 + */ + public $criteria=array(); + + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + + $className=$this->className===null?get_class($object):Yii::import($this->className); + $attributeName=$this->attributeName===null?$attribute:$this->attributeName; + $finder=CActiveRecord::model($className); + $table=$finder->getTableSchema(); + if(($column=$table->getColumn($attributeName))===null) + throw new CException(Yii::t('yii','Column "{column} does not exist in table "{table}".', + array('{column}'=>$attributeName,'{table}'=>$table->name))); + + $columnName=$column->rawName; + $criteria=new CDbCriteria(array( + 'condition'=>$this->caseSensitive ? "$columnName=:value" : "LOWER($columnName)=LOWER(:value)", + 'params'=>array(':value'=>$value), + )); + if($this->criteria!==array()) + $criteria->mergeWith($this->criteria); + + if($column->isPrimaryKey || $this->className!==null) + $exists=$finder->exists($criteria); + else + { + // need to exclude the current record based on PK + $criteria->limit=2; + $objects=$finder->findAll($criteria); + $n=count($objects); + if($n===1) + $exists=$objects[0]->getPrimaryKey()!=$object->getPrimaryKey(); + else + $exists=$n>1; + } + + if($exists) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} "{value}" has already been taken.'); + $this->addError($object,$attribute,$message,array('{value}'=>$value)); + } + } +} + diff --git a/web/framework-1.0.8/validators/CUrlValidator.php b/web/framework-1.0.8/validators/CUrlValidator.php new file mode 100644 index 0000000..afac641 --- /dev/null +++ b/web/framework-1.0.8/validators/CUrlValidator.php @@ -0,0 +1,49 @@ +<?php +/** + * CUrlValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUrlValidator validates that the attribute value is a valid http or https URL. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CUrlValidator.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CUrlValidator extends CValidator +{ + /** + * @var string the regular expression used to validates the attribute value. + */ + public $pattern='/^(http|https):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)/i'; + /** + * @var boolean whether the attribute value can be null or empty. Defaults to true, + * meaning that if the attribute is empty, it is considered valid. + */ + public $allowEmpty=true; + + /** + * Validates the attribute of the object. + * If there is any error, the error message is added to the object. + * @param CModel the object being validated + * @param string the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $value=$object->$attribute; + if($this->allowEmpty && ($value===null || $value==='')) + return; + if(!preg_match($this->pattern,$value)) + { + $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is not a valid URL.'); + $this->addError($object,$attribute,$message); + } + } +} + diff --git a/web/framework-1.0.8/validators/CValidator.php b/web/framework-1.0.8/validators/CValidator.php new file mode 100644 index 0000000..9d265a8 --- /dev/null +++ b/web/framework-1.0.8/validators/CValidator.php @@ -0,0 +1,194 @@ +<?php +/** + * CValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CValidator is the base class for all validators. + * + * Child classes must implement the {@link validateAttribute} method. + * + * The following properties are defined in CValidator: + * <ul> + * <li>{@link attributes}: array, list of attributes to be validated;</li> + * <li>{@link message}: string, the customized error message. The message + * may contain placeholders that will be replaced with the actual content. + * For example, the "{attribute}" placeholder will be replaced with the label + * of the problematic attribute. Different validators may define additional + * placeholders.</li> + * <li>{@link on}: string, in which scenario should the validator be in effect. + * This is used to match the 'on' parameter supplied when calling {@link CModel::validate}.</li> + * </ul> + * + * When using {@link createValidator} to create a validator, the following aliases + * are recognized as the corresponding built-in validator classes: + * <ul> + * <li>required: {@link CRequiredValidator}</li> + * <li>filter: {@link CFilterValidator}</li> + * <li>match: {@link CRegularExpressionValidator}</li> + * <li>email: {@link CEmailValidator}</li> + * <li>url: {@link CUrlValidator}</li> + * <li>unique: {@link CUniqueValidator}</li> + * <li>compare: {@link CCompareValidator}</li> + * <li>length: {@link CStringValidator}</li> + * <li>in: {@link CRangeValidator}</li> + * <li>numerical: {@link CNumberValidator}</li> + * <li>captcha: {@link CCaptchaValidator}</li> + * <li>type: {@link CTypeValidator}</li> + * <li>file: {@link CFileValidator}</li> + * <li>default: {@link CDefaultValueValidator}</li> + * <li>exist: {@link CExistValidator}</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CValidator.php 911 2009-04-03 16:05:18Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +abstract class CValidator extends CComponent +{ + /** + * @var array list of built-in validators (name=>class) + */ + public static $builtInValidators=array( + 'required'=>'CRequiredValidator', + 'filter'=>'CFilterValidator', + 'match'=>'CRegularExpressionValidator', + 'email'=>'CEmailValidator', + 'url'=>'CUrlValidator', + 'unique'=>'CUniqueValidator', + 'compare'=>'CCompareValidator', + 'length'=>'CStringValidator', + 'in'=>'CRangeValidator', + 'numerical'=>'CNumberValidator', + 'captcha'=>'CCaptchaValidator', + 'type'=>'CTypeValidator', + 'file'=>'CFileValidator', + 'default'=>'CDefaultValueValidator', + 'exist'=>'CExistValidator', + ); + + /** + * @var array list of attributes to be validated. + */ + public $attributes; + /** + * @var string the user-defined error message. Different validators may define various + * placeholders in the message that are to be replaced with actual values. All validators + * recognize "{attribute}" placeholder, which will be replaced with the label of the attribute. + */ + public $message; + /** + * @var array list of scenarios that the validator should be applied. + * Each array value refers to a scenario name with the same name as its array key. + */ + public $on; + + /** + * Validates a single attribute. + * This method should be overriden by child classes. + * @param CModel the data object being validated + * @param string the name of the attribute to be validated. + */ + abstract protected function validateAttribute($object,$attribute); + + + /** + * Creates a validator object. + * @param string the name or class of the validator + * @param CModel the data object being validated that may contain the inline validation method + * @param mixed list of attributes to be validated. This can be either an array of + * the attribute names or a string of comma-separated attribute names. + * @param array initial values to be applied to the validator properties + */ + public static function createValidator($name,$object,$attributes,$params) + { + if(is_string($attributes)) + $attributes=preg_split('/[\s,]+/',$attributes,-1,PREG_SPLIT_NO_EMPTY); + + if(isset($params['on'])) + { + if(is_array($params['on'])) + $on=$params['on']; + else + $on=preg_split('/[\s,]+/',$params['on'],-1,PREG_SPLIT_NO_EMPTY); + } + else + $on=array(); + + if(method_exists($object,$name)) + { + $validator=new CInlineValidator; + $validator->attributes=$attributes; + $validator->method=$name; + $validator->params=$params; + } + else + { + $params['attributes']=$attributes; + if(isset(self::$builtInValidators[$name])) + $className=Yii::import(self::$builtInValidators[$name],true); + else + $className=Yii::import($name,true); + $validator=new $className; + foreach($params as $name=>$value) + $validator->$name=$value; + } + + $validator->on=empty($on) ? array() : array_combine($on,$on); + + return $validator; + } + + /** + * Validates the specified object. + * @param CModel the data object being validated + * @param array the list of attributes to be validated. Defaults to null, + * meaning every attribute listed in {@link attributes} will be validated. + */ + public function validate($object,$attributes=null) + { + if(is_array($attributes)) + $attributes=array_intersect($this->attributes,$attributes); + else + $attributes=$this->attributes; + foreach($attributes as $attribute) + $this->validateAttribute($object,$attribute); + } + + /** + * Returns a value indicating whether the validator applies to the specified scenario. + * A validator applies to a scenario as long as any of the following conditions is met: + * <ul> + * <li>the validator's "on" property is empty</li> + * <li>the validator's "on" property contains the specified scenario</li> + * </ul> + * @param string scenario name + * @return boolean whether the validator applies to the specified scenario. + * @since 1.0.2 + */ + public function applyTo($scenario) + { + return empty($this->on) || isset($this->on[$scenario]); + } + + /** + * Adds an error about the specified attribute to the active record. + * This is a helper method that performs message selection and internationalization. + * @param CModel the data object being validated + * @param string the attribute being validated + * @param string the error message + * @param array values for the placeholders in the error message + */ + protected function addError($object,$attribute,$message,$params=array()) + { + $params['{attribute}']=$object->getAttributeLabel($attribute); + $object->addError($attribute,strtr($message,$params)); + } +} + diff --git a/web/framework-1.0.8/vendors/README.html b/web/framework-1.0.8/vendors/README.html new file mode 100644 index 0000000..841a7ac --- /dev/null +++ b/web/framework-1.0.8/vendors/README.html @@ -0,0 +1,89 @@ +<html> +<head> +<title>Third-Party Library List + + + +

Third-Party Library List

+

+This folder includes third-party libraries that are used by the Yii framework. +All these libraries are using licenses that are compatible to the BSD license used by Yii. +This means you can safely use Yii for whatever purpose, provided you comply to the BSD license. +Please refer to the detailed license information as shown below: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Library NameLicenseRelated Yii Component
jQuery 1.3.2MITmost JavaScript-related functionalities
jQuery Autocomplete 1.0.2MITCHtml::autoComplete()
jQuery Masked Input 1.2.0MITCHtml::maskedTextField()
jQuery Multi File Upload 1.29MITCMultiFileUpload
jQuery TreeView 1.4MITCTreeView
jQuery Star Rating 2.61MITCStarRating
PEAR JSONBSDCJSON
Unicode CLDR Data 1.6UnicodeI18N-related functionalities
ADOdb Date LibraryBSDCTimestamp
Text_Highlighter - Generic Syntax Highlighter (v0.7.0 beta)The PHP LicenseCTextHighlighter (note: many PHP files are modified to make them workable in PHP 5 strict mode and their PEAR dependency are also removed.)
PEAR Gettext (v0.4.1 beta)The PHP LicenseCGettextMoFile
HTML Purifier (v3.2.0)LGPLCHtmlPurifier
PHP Markdown Extra (v1.2.2)BSDCMarkdown
+ + + diff --git a/web/framework-1.0.8/vendors/adodb/LICENSE.txt b/web/framework-1.0.8/vendors/adodb/LICENSE.txt new file mode 100644 index 0000000..7d3c8a0 --- /dev/null +++ b/web/framework-1.0.8/vendors/adodb/LICENSE.txt @@ -0,0 +1,182 @@ +ADOdb is dual licensed using BSD and LGPL. + +In plain English, you do not need to distribute your application in source code form, nor do you need to distribute ADOdb source code, provided you follow the rest of terms of the BSD license. + +For more info about ADOdb, visit http://adodb.sourceforge.net/ + +BSD Style-License +================= + +Copyright (c) 2000, 2001, 2002, 2003, 2004 John Lim +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list +of conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. + +Neither the name of the John Lim nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior written +permission. + +DISCLAIMER: +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +JOHN LIM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +========================================================== +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + +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 \ No newline at end of file diff --git a/web/framework-1.0.8/vendors/cldr/LICENSE.txt b/web/framework-1.0.8/vendors/cldr/LICENSE.txt new file mode 100644 index 0000000..9c9bb91 --- /dev/null +++ b/web/framework-1.0.8/vendors/cldr/LICENSE.txt @@ -0,0 +1,33 @@ +Copyright © 1991-2007 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, and/or sell copies of the Data +Files or Software, and to permit persons to whom the Data Files or +Software are furnished to do so, provided that (a) the above copyright +notice(s) and this permission notice appear with all copies of the +Data Files or Software, (b) both the above copyright notice(s) and +this permission notice appear in associated documentation, and (c) +there is clear notice in each modified Data File or in the Software +as well as in the documentation associated with the Data File(s) or +Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR +ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE +DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. \ No newline at end of file diff --git a/web/framework-1.0.8/vendors/gettext/LICENSE.txt b/web/framework-1.0.8/vendors/gettext/LICENSE.txt new file mode 100644 index 0000000..c27fa2d --- /dev/null +++ b/web/framework-1.0.8/vendors/gettext/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2004-2005, Michael Wallner . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/web/framework-1.0.8/vendors/json/LICENSE.txt b/web/framework-1.0.8/vendors/json/LICENSE.txt new file mode 100644 index 0000000..6b2c70e --- /dev/null +++ b/web/framework-1.0.8/vendors/json/LICENSE.txt @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/web/framework-1.0.8/vendors/markdown/LICENSE.txt b/web/framework-1.0.8/vendors/markdown/LICENSE.txt new file mode 100644 index 0000000..52c868b --- /dev/null +++ b/web/framework-1.0.8/vendors/markdown/LICENSE.txt @@ -0,0 +1,36 @@ +PHP Markdown & Extra +Copyright (c) 2004-2008 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/web/framework-1.0.8/vendors/markdown/markdown.php b/web/framework-1.0.8/vendors/markdown/markdown.php new file mode 100644 index 0000000..1ed9db0 --- /dev/null +++ b/web/framework-1.0.8/vendors/markdown/markdown.php @@ -0,0 +1,2623 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# + + +# +# Markdown Parser Class +# + +class Markdown_Parser { + + # Regex to match balanced [brackets]. + # Needed to insert a maximum bracked depth while converting to PHP. + public $nested_brackets_depth = 6; + public $nested_brackets_re; + + public $nested_url_parenthesis_depth = 4; + public $nested_url_parenthesis_re; + + # Table of hash values for escaped characters: + public $escape_chars = '\`*_{}[]()>#+-.!'; + public $escape_chars_re; + + # Change to ">" for HTML output. + public $empty_element_suffix = ' />'; + public $tab_width = 4; + + # Change to `true` to disallow markup or entities. + public $no_markup = false; + public $no_entities = false; + + # Predefined urls and titles for reference links and images. + public $predef_urls = array(); + public $predef_titles = array(); + + public function __construct() { + # + # Constructor function. Initialize appropriate member variables. + # + $this->_initDetab(); + $this->prepareItalicsAndBold(); + + $this->nested_brackets_re = + str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). + str_repeat('\])*', $this->nested_brackets_depth); + + $this->nested_url_parenthesis_re = + str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). + str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); + + $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; + + # Sort document, block, and span gamut in ascendent priority order. + asort($this->document_gamut); + asort($this->block_gamut); + asort($this->span_gamut); + } + + + # Internal hashes used during transformation. + public $urls = array(); + public $titles = array(); + public $html_hashes = array(); + + # Status flag to avoid invalid nesting. + public $in_anchor = false; + + + public function setup() { + # + # Called before the transformation process starts to setup parser + # states. + # + # Clear global hashes. + $this->urls = $this->predef_urls; + $this->titles = $this->predef_titles; + $this->html_hashes = array(); + + $in_anchor = false; + } + + public function teardown() { + # + # Called after the transformation process to clear any variable + # which may be taking up memory unnecessarly. + # + $this->urls = array(); + $this->titles = array(); + $this->html_hashes = array(); + } + + + public function transform($text) { + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + $this->setup(); + + # Remove UTF-8 BOM and marker character in input, if present. + $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = preg_replace('{\r\n?}', "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->detab($text); + + # Turn block-level HTML blocks into hash entries + $text = $this->hashHTMLBlocks($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ ]*\n+/ . + $text = preg_replace('/^[ ]+$/m', '', $text); + + # Run document gamut methods. + foreach ($this->document_gamut as $method => $priority) { + $text = $this->$method($text); + } + + $this->teardown(); + + return $text . "\n"; + } + + public $document_gamut = array( + # Strip link definitions, store in hashes. + "stripLinkDefinitions" => 20, + + "runBasicBlockGamut" => 30, + ); + + + public function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + ? # url = $2 + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $3 + [")] + [ ]* + )? # title is optional + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + public function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $this->urls[$link_id] = $matches[2]; + $this->titles[$link_id] =& $matches[3]; + return ''; # String that will replace the block + } + + + public function hashHTMLBlocks($text) { + if ($this->no_markup) return $text; + + $less_than_tab = $this->tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + # + # * List "a" is made of tags which can be both inline or block-level. + # These will be treated block-level when the start tag is alone on + # its line, otherwise they're not matched here and will be taken as + # inline later. + # * List "b" is made of tags which are always block-level; + # + $block_tags_a_re = 'ins|del'; + $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. + 'script|noscript|form|fieldset|iframe|math'; + + # Regular expression for the content of a block tag. + $nested_tags_level = 4; + $attr = ' + (?> # optional tag attributes + \s # starts with whitespace + (?> + [^>"/]+ # text outside quotes + | + /+(?!>) # slash not followed by ">" + | + "[^"]*" # text inside double quotes (tolerate ">") + | + \'[^\']*\' # text inside single quotes (tolerate ">") + )* + )? + '; + $content = + str_repeat(' + (?> + [^<]+ # content without tag + | + <\2 # nested opening tag + '.$attr.' # attributes + (?> + /> + | + >', $nested_tags_level). # end of opening tag + '.*?'. # last level nested tag content + str_repeat(' + # closing nested tag + ) + | + <(?!/\2\s*> # other tags with a different name + ) + )*', + $nested_tags_level); + $content2 = str_replace('\2', '\3', $content); + + # First, look for nested blocks, e.g.: + #

+ #
+ # tags for inner block must be indented. + #
+ #
+ # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
` and stop at the first `
`. + $text = preg_replace_callback('{(?> + (?> + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + + # Match from `\n` to `\n`, handling nested tags + # in between. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_b_re.')# start tag = $2 + '.$attr.'> # attributes followed by > and \n + '.$content.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special version for tags of group a. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_a_re.')# start tag = $3 + '.$attr.'>[ ]*\n # attributes followed by > + '.$content2.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special case just for
. It was easier to make a special + # case than to make the other regex more complicated. + + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + '.$attr.' # attributes + /?> # the matching end tag + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # Special case for standalone HTML comments: + + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # PHP and ASP-style processor instructions ( + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + ) + )}Sxmi', + array(&$this, '_hashHTMLBlocks_callback'), + $text); + + return $text; + } + public function _hashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = $this->hashBlock($text); + return "\n\n$key\n\n"; + } + + + public function hashPart($text, $boundary = 'X') { + # + # Called whenever a tag must be hashed when a function insert an atomic + # element in the text stream. Passing $text to through this function gives + # a unique text-token which will be reverted back when calling unhash. + # + # The $boundary argument specify what character should be used to surround + # the token. By convension, "B" is used for block elements that needs not + # to be wrapped into paragraph tags at the end, ":" is used for elements + # that are word separators and "X" is used in the general case. + # + # Swap back any tag hash found in $text so we do not have to `unhash` + # multiple times at the end. + $text = $this->unhash($text); + + # Then hash the block. + static $i = 0; + $key = "$boundary\x1A" . ++$i . $boundary; + $this->html_hashes[$key] = $text; + return $key; # String that will replace the tag. + } + + + public function hashBlock($text) { + # + # Shortcut function for hashPart with block-level boundaries. + # + return $this->hashPart($text, 'B'); + } + + + public $block_gamut = array( + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + "doHeaders" => 10, + "doHorizontalRules" => 20, + + "doLists" => 40, + "doCodeBlocks" => 50, + "doBlockQuotes" => 60, + ); + + public function runBlockGamut($text) { + # + # Run block gamut tranformations. + # + # We need to escape raw HTML in Markdown source before doing anything + # else. This need to be done for each block, and not only at the + # begining in the Markdown function since hashed blocks can be part of + # list items and could have been indented. Indented blocks would have + # been seen as a code block in a previous pass of hashHTMLBlocks. + $text = $this->hashHTMLBlocks($text); + + return $this->runBasicBlockGamut($text); + } + + public function runBasicBlockGamut($text) { + # + # Run block gamut tranformations, without hashing HTML blocks. This is + # useful when HTML blocks are known to be already hashed, like in the first + # whole-document pass. + # + foreach ($this->block_gamut as $method => $priority) { + $text = $this->$method($text); + } + + # Finally form paragraph and restore hashed blocks. + $text = $this->formParagraphs($text); + + return $text; + } + + + public function doHorizontalRules($text) { + # Do Horizontal Rules: + return preg_replace( + '{ + ^[ ]{0,3} # Leading space + ([-*_]) # $1: First marker + (?> # Repeated marker group + [ ]{0,2} # Zero, one, or two spaces. + \1 # Marker character + ){2,} # Group repeated at least twice + [ ]* # Tailing spaces + $ # End of line. + }mx', + "\n".$this->hashBlock("empty_element_suffix")."\n", + $text); + } + + + public $span_gamut = array( + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + # Process character escapes, code spans, and inline HTML + # in one shot. + "parseSpan" => -30, + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + "doImages" => 10, + "doAnchors" => 20, + + # Make links out of things like `` + # Must come after doAnchors, because you can use < and > + # delimiters in inline links like [this](). + "doAutoLinks" => 30, + "encodeAmpsAndAngles" => 40, + + "doItalicsAndBold" => 50, + "doHardBreaks" => 60, + ); + + public function runSpanGamut($text) { + # + # Run span gamut tranformations. + # + foreach ($this->span_gamut as $method => $priority) { + $text = $this->$method($text); + } + + return $text; + } + + + public function doHardBreaks($text) { + # Do hard breaks: + return preg_replace_callback('/ {2,}\n/', + array(&$this, '_doHardBreaks_callback'), $text); + } + public function _doHardBreaks_callback($matches) { + return $this->hashPart("empty_element_suffix\n"); + } + + + public function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ ]* + (?: + <(\S*)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ ]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ ]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + ) + }xs', + array(&$this, '_DoAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link test][1] + # or [link test](/foo) + # +// $text = preg_replace_callback('{ +// ( # wrap whole match in $1 +// \[ +// ([^\[\]]+) # link text = $2; can\'t contain [ or ] +// \] +// ) +// }xs', +// array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + public function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + public function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + public function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ ]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ ]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ ]* + )? # title is optional + \) + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + public function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + public function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + public function doHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + public function _doHeaders_callback_setext($matches) { + # Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) + return $matches[0]; + + $level = $matches[2]{0} == '=' ? 1 : 2; + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + public function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + public function doLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $markers_relist = array($marker_ul_re, $marker_ol_re); + + foreach ($markers_relist as $marker_re) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list_re = ' + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ('.$marker_re.') # $3 = first list item marker + [ ]+ + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ ]* + '.$marker_re.'[ ]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n)\n|\A\n?) # Must eat the newline + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + } + + return $text; + } + public function _doLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul_re/", $matches[3]) ? "ul" : "ol"; + + $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); + + $list .= "\n"; + $result = $this->processListItems($list, $marker_any_re); + + $result = $this->hashBlock("<$list_type>\n" . $result . ""); + return "\n". $result ."\n\n"; + } + + public $list_level = 0; + + public function processListItems($list_str, $marker_any_re) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + # The $this->list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ ]*) # leading whitespace = $2 + ('.$marker_any_re.' # list marker and space = $3 + (?:[ ]+|(?=\n)) # space only required if item is not empty + ) + ((?s:.*?)) # list item text = $4 + (?:(\n+(?=\n))|\n) # tailing blank line = $5 + (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) + }xm', + array(&$this, '_processListItems_callback'), $list_str); + + $this->list_level--; + return $list_str; + } + public function _processListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + $marker_space = $matches[3]; + $tailing_blank_line =& $matches[5]; + + if ($leading_line || $tailing_blank_line || + preg_match('/\n{2,}/', $item)) + { + # Replace marker with the appropriate whitespace indentation + $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; + $item = $this->runBlockGamut($this->outdent($item)."\n"); + } + else { + # Recursion for sub-lists: + $item = $this->doLists($this->outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->runSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + public function doCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A\n?)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?>
    +					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array(&$this, '_doCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	public function _doCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->outdent($codeblock);
    +		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    +
    +		# trim leading newlines and trailing newlines
    +		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    +
    +		$codeblock = "
    $codeblock\n
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + + + public function makeCodeSpan($code) { + # + # Create a code span markup for $code. Called from handleSpanToken. + # + $code = htmlspecialchars(trim($code), ENT_NOQUOTES); + return $this->hashPart("$code"); + } + + + public $em_relist = array( + '' => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(?em_relist as $em => $em_re) { + foreach ($this->strong_relist as $strong => $strong_re) { + # Construct list of allowed token expressions. + $token_relist = array(); + if (isset($this->em_strong_relist["$em$strong"])) { + $token_relist[] = $this->em_strong_relist["$em$strong"]; + } + $token_relist[] = $em_re; + $token_relist[] = $strong_re; + + # Construct master expression from list. + $token_re = '{('. implode('|', $token_relist) .')}'; + $this->em_strong_prepared_relist["$em$strong"] = $token_re; + } + } + } + + public function doItalicsAndBold($text) { + $token_stack = array(''); + $text_stack = array(''); + $em = ''; + $strong = ''; + $tree_char_em = false; + + while (1) { + # + # Get prepared regular expression for seraching emphasis tokens + # in current context. + # + $token_re = $this->em_strong_prepared_relist["$em$strong"]; + + # + # Each loop iteration seach for the next emphasis token. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text_stack[0] .= $parts[0]; + $token =& $parts[1]; + $text =& $parts[2]; + + if (empty($token)) { + # Reached end of text span: empty stack without emitting. + # any more emphasis. + while ($token_stack[0]) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + break; + } + + $token_len = strlen($token); + if ($tree_char_em) { + # Reached closing marker while inside a three-char emphasis. + if ($token_len == 3) { + # Three-char closing marker, close em and strong. + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + $strong = ''; + } else { + # Other closing marker: close one em or strong and + # change current token state to match the other + $token_stack[0] = str_repeat($token{0}, 3-$token_len); + $tag = $token_len == 2 ? "strong" : "em"; + $span = $text_stack[0]; + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] = $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + $tree_char_em = false; + } else if ($token_len == 3) { + if ($em) { + # Reached closing marker for both em and strong. + # Closing strong marker: + for ($i = 0; $i < 2; ++$i) { + $shifted_token = array_shift($token_stack); + $tag = strlen($shifted_token) == 2 ? "strong" : "em"; + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] .= $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + } else { + # Reached opening three-char emphasis marker. Push on token + # stack; will be handled by the special condition above. + $em = $token{0}; + $strong = "$em$em"; + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $tree_char_em = true; + } + } else if ($token_len == 2) { + if ($strong) { + # Unwind any dangling emphasis marker: + if (strlen($token_stack[0]) == 1) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + # Closing strong marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $strong = ''; + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $strong = $token; + } + } else { + # Here $token_len == 1 + if ($em) { + if (strlen($token_stack[0]) == 1) { + # Closing emphasis marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + } else { + $text_stack[0] .= $token; + } + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $em = $token; + } + } + } + return $text_stack[0]; + } + + + public function doBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + (?> + ^[ ]*>[ ]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array(&$this, '_doBlockQuotes_callback'), $text); + + return $text; + } + public function _doBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); + $bq = $this->runBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces cause problem with
     content,
    +		# so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array(&$this, '_DoBlockQuotes_callback2'), $bq); + + return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; + } + public function _doBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + public function formParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { + # Is a paragraph. + $value = $this->runSpanGamut($value); + $value = preg_replace('/^([ ]*)/', "

    ", $value); + $value .= "

    "; + $grafs[$key] = $this->unhash($value); + } + else { + # Is a block. + # Modify elements of @grafs in-place... + $graf = $value; + $block = $this->html_hashes[$graf]; + $graf = $block; +// if (preg_match('{ +// \A +// ( # $1 =
    tag +//
    ]* +// \b +// markdown\s*=\s* ([\'"]) # $2 = attr quote char +// 1 +// \2 +// [^>]* +// > +// ) +// ( # $3 = contents +// .* +// ) +// (
    ) # $4 = closing tag +// \z +// }xs', $block, $matches)) +// { +// list(, $div_open, , $div_content, $div_close) = $matches; +// +// # We can't call Markdown(), because that resets the hash; +// # that initialization code should be pulled into its own sub, though. +// $div_content = $this->hashHTMLBlocks($div_content); +// +// # Run document gamut methods on the content. +// foreach ($this->document_gamut as $method => $priority) { +// $div_content = $this->$method($div_content); +// } +// +// $div_open = preg_replace( +// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); +// +// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; +// } + $grafs[$key] = $graf; + } + } + + return implode("\n\n", $grafs); + } + + + public function encodeAttribute($text) { + # + # Encode text for a double-quoted HTML attribute. This function + # is *not* suitable for attributes enclosed in single quotes. + # + $text = $this->encodeAmpsAndAngles($text); + $text = str_replace('"', '"', $text); + return $text; + } + + + public function encodeAmpsAndAngles($text) { + # + # Smart processing for ampersands and angle brackets that need to + # be encoded. Valid character entities are left alone unless the + # no-entities mode is set. + # + if ($this->no_entities) { + $text = str_replace('&', '&', $text); + } else { + # Ampersand-encoding based entirely on Nat Irons's Amputator + # MT plugin: + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + } + # Encode remaining <'s + $text = str_replace('<', '<', $text); + + return $text; + } + + + public function doAutoLinks($text) { + $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', + array(&$this, '_doAutoLinks_url_callback'), $text); + + # Email addresses: + $text = preg_replace_callback('{ + < + (?:mailto:)? + ( + [-.\w\x80-\xFF]+ + \@ + [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ + ) + > + }xi', + array(&$this, '_doAutoLinks_email_callback'), $text); + + return $text; + } + public function _doAutoLinks_url_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $link = "$url"; + return $this->hashPart($link); + } + public function _doAutoLinks_email_callback($matches) { + $address = $matches[1]; + $link = $this->encodeEmailAddress($address); + return $this->hashPart($link); + } + + + public function encodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + #

    foo@exampl + # e.com

    + # + # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. + # With some optimizations by Milian Wolff. + # + $addr = "mailto:" . $addr; + $chars = preg_split('/(? $char) { + $ord = ord($char); + # Ignore non-ascii chars. + if ($ord < 128) { + $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') /* do nothing */; + else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; + else $chars[$key] = '&#'.$ord.';'; + } + } + + $addr = implode('', $chars); + $text = implode('', array_slice($chars, 7)); # text without `mailto:` + $addr = "$text"; + + return $addr; + } + + + public function parseSpan($str) { + # + # Take the string $str and parse it into tokens, hashing embeded HTML, + # escaped characters and handling code spans. + # + $output = ''; + + $span_re = '{ + ( + \\\\'.$this->escape_chars_re.' + | + (?no_markup ? '' : ' + | + # comment + | + <\?.*?\?> | <%.*?%> # processing instruction + | + <[/!$]?[-a-zA-Z0-9:]+ # regular tags + (?> + \s + (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* + )? + > + ').' + ) + }xs'; + + while (1) { + # + # Each loop iteration seach for either the next tag, the next + # openning code span marker, or the next escaped character. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); + + # Create token from text preceding tag. + if ($parts[0] != "") { + $output .= $parts[0]; + } + + # Check if we reach the end. + if (isset($parts[1])) { + $output .= $this->handleSpanToken($parts[1], $parts[2]); + $str = $parts[2]; + } + else { + break; + } + } + + return $output; + } + + + public function handleSpanToken($token, &$str) { + # + # Handle $token provided by parseSpan by determining its nature and + # returning the corresponding value that should replace it. + # + switch ($token{0}) { + case "\\": + return $this->hashPart("&#". ord($token{1}). ";"); + case "`": + # Search for end marker in remaining text. + if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', + $str, $matches)) + { + $str = $matches[2]; + $codespan = $this->makeCodeSpan($matches[1]); + return $this->hashPart($codespan); + } + return $token; // return as text since no ending marker found. + default: + return $this->hashPart($token); + } + } + + + public function outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); + } + + + # String length function for detab. `_initDetab` will create a function to + # hanlde UTF-8 if the default function does not exist. + public $utf8_strlen = 'mb_strlen'; + + public function detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $text = preg_replace_callback('/^.*\t.*$/m', + array(&$this, '_detab_callback'), $text); + + return $text; + } + public function _detab_callback($matches) { + $line = $matches[0]; + $strlen = $this->utf8_strlen; # strlen function for UTF-8. + + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->tab_width - + $strlen($line, 'UTF-8') % $this->tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + return $line; + } + public function _initDetab() { + # + # Check for the availability of the function in the `utf8_strlen` property + # (initially `mb_strlen`). If the function is not available, create a + # function that will loosely count the number of UTF-8 characters with a + # regular expression. + # + if (function_exists($this->utf8_strlen)) return; + $this->utf8_strlen = create_function('$text', 'return preg_match_all( + "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", + $text, $m);'); + } + + + public function unhash($text) { + # + # Swap back in all the tags hashed by _HashHTMLBlocks. + # + return preg_replace_callback('/(.)\x1A[0-9]+\1/', + array(&$this, '_unhash_callback'), $text); + } + public function _unhash_callback($matches) { + return $this->html_hashes[$matches[0]]; + } + +} + + +# +# Markdown Extra Parser Class +# + +class MarkdownExtra_Parser extends Markdown_Parser { + + # Prefix for footnote ids. + public $fn_id_prefix = ""; + + # Optional title attribute for footnote links and backlinks. + public $fn_link_title = ''; + public $fn_backlink_title = ''; + + # Optional class attribute for footnote links and backlinks. + public $fn_link_class = ''; + public $fn_backlink_class = ''; + + # Predefined abbreviations. + public $predef_abbr = array(); + + + public function __construct() { + # + # Constructor function. Initialize the parser object. + # + # Add extra escapable characters before parent constructor + # initialize the table. + $this->escape_chars .= ':|'; + + # Insert extra document, block, and span transformations. + # Parent constructor will do the sorting. + $this->document_gamut += array( + "doFencedCodeBlocks" => 5, + "stripFootnotes" => 15, + "stripAbbreviations" => 25, + "appendFootnotes" => 50, + ); + $this->block_gamut += array( + "doFencedCodeBlocks" => 5, + "doTables" => 15, + "doDefLists" => 45, + ); + $this->span_gamut += array( + "doFootnotes" => 5, + "doAbbreviations" => 70, + ); + + parent::__construct(); + } + + + # Extra variables used during extra transformations. + public $footnotes = array(); + public $footnotes_ordered = array(); + public $abbr_desciptions = array(); + public $abbr_word_re = ''; + + # Give the current footnote number. + public $footnote_counter = 1; + + + public function setup() { + # + # Setting up Extra-specific variables. + # + parent::setup(); + + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + $this->footnote_counter = 1; + + foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + } + } + + public function teardown() { + # + # Clearing Extra-specific variables. + # + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + + parent::teardown(); + } + + + ### HTML Block Parser ### + + # Tags that are always treated as block tags: + public $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend'; + + # Tags treated as block tags only if the opening tag is alone on it's line: + public $context_block_tags_re = 'script|noscript|math|ins|del'; + + # Tags where markdown="1" default to span mode: + public $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; + + # Tags which must not have their contents modified, no matter where + # they appear: + public $clean_tags_re = 'script|math'; + + # Tags that do not need to be closed. + public $auto_close_tags_re = 'hr|img'; + + + public function hashHTMLBlocks($text) { + # + # Hashify HTML Blocks and "clean tags". + # + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded. + # + # This works by calling _HashHTMLBlocks_InMarkdown, which then calls + # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" + # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back + # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. + # These two functions are calling each other. It's recursive! + # + # + # Call the HTML-in-Markdown hasher. + # + list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); + + return $text; + } + public function _hashHTMLBlocks_inMarkdown($text, $indent = 0, + $enclosing_tag_re = '', $span = false) + { + # + # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. + # + # * $indent is the number of space to be ignored when checking for code + # blocks. This is important because if we don't take the indent into + # account, something like this (which looks right) won't work as expected: + # + #

    + #
    + # Hello World. <-- Is this a Markdown code block or text? + #
    <-- Is this a Markdown code block or a real tag? + #
    + # + # If you don't like this, just don't indent the tag on which + # you apply the markdown="1" attribute. + # + # * If $enclosing_tag_re is not empty, stops at the first unmatched closing + # tag with that name. Nested tags supported. + # + # * If $span is true, text inside must treated as span. So any double + # newline will be replaced by a single newline so that it does not create + # paragraphs. + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to check for the presense of newlines around a block tag. + $newline_before_re = '/(?:^\n?|\n\n)*$/'; + $newline_after_re = + '{ + ^ # Start of text following the tag. + (?>[ ]*)? # Optional comment. + [ ]*\n # Must be followed by newline. + }xs'; + + # Regex to match any tag. + $block_tag_re = + '{ + ( # $2: Capture hole tag. + # Tag name. + '.$this->block_tags_re.' | + '.$this->context_block_tags_re.' | + '.$this->clean_tags_re.' | + (?!\s)'.$enclosing_tag_re.' + ) + (?: + (?=[\s"\'/]) # Allowed characters after tag name. + (?> + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + | + # Code span marker + `+ + '. ( !$span ? ' # If not in span. + | + # Indented code block + (?> ^[ ]*\n? | \n[ ]*\n ) + [ ]{'.($indent+4).'}[^\n]* \n + (?> + (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n + )* + | + # Fenced code block marker + (?> ^ | \n ) + [ ]{'.($indent).'}~~~+[ ]*\n + ' : '' ). ' # End (if not is span). + ) + }xs'; + + + $depth = 0; # Current depth inside the tag tree. + $parsed = ""; # Parsed text that will be returned. + + # + # Loop through every tag until we find the closing tag of the parent + # or loop until reaching the end of text if no parent tag specified. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($block_tag_re, $text, 2, + PREG_SPLIT_DELIM_CAPTURE); + + # If in Markdown span mode, add a empty-string span-level hash + # after each newline to prevent triggering any block element. + if ($span) { + $void = $this->hashPart("", ':'); + $newline = "$void\n"; + $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; + } + + $parsed .= $parts[0]; # Text before current tag. + + # If end of $text has been reached. Stop loop. + if (count($parts) < 3) { + $text = ""; + break; + } + + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + $tag_re = preg_quote($tag); # For use in a regular expression. + + # + # Check for: Code span marker + # + if ($tag{0} == "`") { + # Find corresponding end marker. + $tag_re = preg_quote($tag); + if (preg_match('{^(?>.+?|\n(?!\n))*?(?.*\n)+?'.$tag_re.' *\n}', $text, + $matches)) + { + # End marker found: pass text unchanged until marker. + $parsed .= $tag . $matches[0]; + $text = substr($text, strlen($matches[0])); + } + else { + # No end marker: just skip it. + $parsed .= $tag; + } + } + } + # + # Check for: Opening Block level tag or + # Opening Context Block tag (like ins and del) + # used as a block tag (tag is alone on it's line). + # + else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) || + ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) && + preg_match($newline_before_re, $parsed) && + preg_match($newline_after_re, $text) ) + ) + { + # Need to parse tag and following text using the HTML parser. + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); + + # Make sure it stays outside of any paragraph by adding newlines. + $parsed .= "\n\n$block_text\n\n"; + } + # + # Check for: Clean tag (like script, math) + # HTML Comments, processing instructions. + # + else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Need to parse tag and following text using the HTML parser. + # (don't check for markdown attribute) + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); + + $parsed .= $block_text; + } + # + # Check for: Tag with same name as enclosing tag. + # + else if ($enclosing_tag_re !== '' && + # Same name as enclosing tag. + preg_match('{^= 0); + + return array($parsed, $text); + } + public function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { + # + # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. + # + # * Calls $hash_method to convert any blocks. + # * Stops when the first opening tag closes. + # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. + # (it is not inside clean tags) + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to match `markdown` attribute inside of a tag. + $markdown_attr_re = ' + { + \s* # Eat whitespace before the `markdown` attribute + markdown + \s*=\s* + (?> + (["\']) # $1: quote delimiter + (.*?) # $2: attribute value + \1 # matching delimiter + | + ([^\s>]*) # $3: unquoted attribute value + ) + () # $4: make $3 always defined (avoid warnings) + }xs'; + + # Regex to match any tag. + $tag_re = '{ + ( # $2: Capture hole tag. + + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + ) + }xs'; + + $original_text = $text; # Save original text in case of faliure. + + $depth = 0; # Current depth inside the tag tree. + $block_text = ""; # Temporary text holder for current text. + $parsed = ""; # Parsed text that will be returned. + + # + # Get the name of the starting tag. + # (This pattern makes $base_tag_name_re safe without quoting.) + # + if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) + $base_tag_name_re = $matches[1]; + + # + # Loop through every tag until we find the corresponding closing tag. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + + if (count($parts) < 3) { + # + # End of $text reached with unbalenced tag(s). + # In that case, we return original text unchanged and pass the + # first character as filtered to prevent an infinite loop in the + # parent function. + # + return array($original_text{0}, substr($original_text, 1)); + } + + $block_text .= $parts[0]; # Text before current tag. + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + + # + # Check for: Auto-close tag (like
    ) + # Comments and Processing Instructions. + # + if (preg_match('{^auto_close_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Just add the tag to the block as if it was text. + $block_text .= $tag; + } + else { + # + # Increase/decrease nested tag count. Only do so if + # the tag's name match base tag's. + # + if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; + $span_mode = $this->mode == 'span' || $this->mode != 'block' && + preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); + + # Calculate indent before tag. + if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { + $strlen = $this->utf8_strlen; + $indent = $strlen($matches[1], 'UTF-8'); + } else { + $indent = 0; + } + + # End preceding block with this tag. + $block_text .= $tag; + $parsed .= $this->$hash_method($block_text); + + # Get enclosing tag name for the ParseMarkdown function. + # (This pattern makes $tag_name_re safe without quoting.) + preg_match('/^<([\w:$]*)\b/', $tag, $matches); + $tag_name_re = $matches[1]; + + # Parse the content using the HTML-in-Markdown parser. + list ($block_text, $text) + = $this->_hashHTMLBlocks_inMarkdown($text, $indent, + $tag_name_re, $span_mode); + + # Outdent markdown text. + if ($indent > 0) { + $block_text = preg_replace("/^[ ]{1,$indent}/m", "", + $block_text); + } + + # Append tag content to parsed text. + if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; + else $parsed .= "$block_text"; + + # Start over a new block. + $block_text = ""; + } + else $block_text .= $tag; + } + + } while ($depth > 0); + + # + # Hash last block text that wasn't processed inside the loop. + # + $parsed .= $this->$hash_method($block_text); + + return array($parsed, $text); + } + + + public function hashClean($text) { + # + # Called whenever a tag must be hashed when a function insert a "clean" tag + # in $text, it pass through this function and is automaticaly escaped, + # blocking invalid nested overlap. + # + return $this->hashPart($text, 'C'); + } + + + public function doHeaders($text) { + # + # Redefined to add id attribute support. + # + # Setext-style headers: + # Header 1 {#header1} + # ======== + # + # Header 2 {#header2} + # -------- + # + $text = preg_replace_callback( + '{ + (^.+?) # $1: Header text + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute + [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer + }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 {#header1} + # ## Header 2 {#header2} + # ## Header 2 with closing hashes ## {#header3} + # ... + # ###### Header 6 {#header2} + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute + [ ]* + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + public function _doHeaders_attr($attr) { + if (empty($attr)) return ""; + return " id=\"$attr\""; + } + public function _doHeaders_callback_setext($matches) { + if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) + return $matches[0]; + $level = $matches[3]{0} == '=' ? 1 : 2; + $attr = $this->_doHeaders_attr($id =& $matches[2]); + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + public function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $attr = $this->_doHeaders_attr($id =& $matches[3]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + public function doTables($text) { + # + # Form HTML tables. + # + $less_than_tab = $this->tab_width - 1; + # + # Find tables with leading pipe. + # + # | Header 1 | Header 2 + # | -------- | -------- + # | Cell 1 | Cell 2 + # | Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] # Optional leading pipe (present) + (.+) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + [ ]* # Allowed whitespace. + [|] .* \n # Row content. + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_doTable_leadingPipe_callback'), $text); + + # + # Find tables without leading pipe. + # + # Header 1 | Header 2 + # -------- | -------- + # Cell 1 | Cell 2 + # Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + (\S.*[|].*) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + .* [|] .* \n # Row content + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_DoTable_callback'), $text); + + return $text; + } + public function _doTable_leadingPipe_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove leading pipe for each row. + $content = preg_replace('/^ *[|]/m', '', $content); + + return $this->_doTable_callback(array($matches[0], $head, $underline, $content)); + } + public function _doTable_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove any tailing pipes for each line. + $head = preg_replace('/[|] *$/m', '', $head); + $underline = preg_replace('/[|] *$/m', '', $underline); + $content = preg_replace('/[|] *$/m', '', $content); + + # Reading alignement from header underline. + $separators = preg_split('/ *[|] */', $underline); + foreach ($separators as $n => $s) { + if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"'; + else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"'; + else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"'; + else $attr[$n] = ''; + } + + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $head = $this->parseSpan($head); + $headers = preg_split('/ *[|] */', $head); + $col_count = count($headers); + + # Write column headers. + $text = "\n"; + $text .= "\n"; + $text .= "\n"; + foreach ($headers as $n => $header) + $text .= " ".$this->runSpanGamut(trim($header))."\n"; + $text .= "\n"; + $text .= "\n"; + + # Split content by row. + $rows = explode("\n", trim($content, "\n")); + + $text .= "\n"; + foreach ($rows as $row) { + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $row = $this->parseSpan($row); + + # Split row by cell. + $row_cells = preg_split('/ *[|] */', $row, $col_count); + $row_cells = array_pad($row_cells, $col_count, ''); + + $text .= "\n"; + foreach ($row_cells as $n => $cell) + $text .= " ".$this->runSpanGamut(trim($cell))."\n"; + $text .= "\n"; + } + $text .= "\n"; + $text .= "
    "; + + return $this->hashBlock($text) . "\n"; + } + + + public function doDefLists($text) { + # + # Form HTML definition lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable pattern to match any entire dl list: + $whole_list_re = '(?> + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ((?>.*\S.*\n)+) # $3 = defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another term + [ ]{0,'.$less_than_tab.'} + (?: \S.*\n )+? # defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?! # Negative lookahead for another definition + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + ) + ) + )'; // mx + + $text = preg_replace_callback('{ + (?>\A\n?|(?<=\n\n)) + '.$whole_list_re.' + }mx', + array(&$this, '_doDefLists_callback'), $text); + + return $text; + } + public function _doDefLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $list = $matches[1]; + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $result = trim($this->processDefListItems($list)); + $result = "
    \n" . $result . "\n
    "; + return $this->hashBlock($result) . "\n\n"; + } + + + public function processDefListItems($list_str) { + # + # Process the contents of a single definition list, splitting it + # into individual term and definition list items. + # + $less_than_tab = $this->tab_width - 1; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + # Process definition terms. + $list_str = preg_replace_callback('{ + (?>\A\n?|\n\n+) # leading line + ( # definition terms = $1 + [ ]{0,'.$less_than_tab.'} # leading whitespace + (?![:][ ]|[ ]) # negative lookahead for a definition + # mark (colon) or more whitespace. + (?> \S.* \n)+? # actual term (not whitespace). + ) + (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed + # with a definition mark. + }xm', + array(&$this, '_processDefListItems_callback_dt'), $list_str); + + # Process actual definitions. + $list_str = preg_replace_callback('{ + \n(\n+)? # leading line = $1 + ( # marker space = $2 + [ ]{0,'.$less_than_tab.'} # whitespace before colon + [:][ ]+ # definition mark (colon) + ) + ((?s:.+?)) # definition text = $3 + (?= \n+ # stop at next definition mark, + (?: # next term or end of text + [ ]{0,'.$less_than_tab.'} [:][ ] | +
    | \z + ) + ) + }xm', + array(&$this, '_processDefListItems_callback_dd'), $list_str); + + return $list_str; + } + public function _processDefListItems_callback_dt($matches) { + $terms = explode("\n", trim($matches[1])); + $text = ''; + foreach ($terms as $term) { + $term = $this->runSpanGamut(trim($term)); + $text .= "\n
    " . $term . "
    "; + } + return $text . "\n"; + } + public function _processDefListItems_callback_dd($matches) { + $leading_line = $matches[1]; + $marker_space = $matches[2]; + $def = $matches[3]; + + if ($leading_line || preg_match('/\n{2,}/', $def)) { + # Replace marker with the appropriate whitespace indentation + $def = str_repeat(' ', strlen($marker_space)) . $def; + $def = $this->runBlockGamut($this->outdent($def . "\n\n")); + $def = "\n". $def ."\n"; + } + else { + $def = rtrim($def); + $def = $this->runSpanGamut($this->outdent($def)); + } + + return "\n
    " . $def . "
    \n"; + } + + + public function doFencedCodeBlocks($text) { + # + # Adding the fenced code block syntax to regular Markdown: + # + # ~~~ + # Code block + # ~~~ + # + $less_than_tab = $this->tab_width; + + $text = preg_replace_callback('{ + (?:\n|\A) + # 1: Opening marker + ( + ~{3,} # Marker: three tilde or more. + ) + [ ]* \n # Whitespace and newline following marker. + + # 2: Content + ( + (?> + (?!\1 [ ]* \n) # Not a closing marker. + .*\n+ + )+ + ) + + # Closing marker. + \1 [ ]* \n + }xm', + array(&$this, '_doFencedCodeBlocks_callback'), $text); + + return $text; + } + public function _doFencedCodeBlocks_callback($matches) { + $codeblock = $matches[2]; + $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); + $codeblock = preg_replace_callback('/^\n+/', + array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); + $codeblock = "
    $codeblock
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + public function _doFencedCodeBlocks_newlines($matches) { + return str_repeat("empty_element_suffix", + strlen($matches[0])); + } + + + # + # Redefining emphasis markers so that emphasis by underscore does not + # work in the middle of a word. + # + public $em_relist = array( + '' => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + $value = trim($this->runSpanGamut($value)); + + # Check if this should be enclosed in a paragraph. + # Clean tag hashes & block tag hashes are left alone. + $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); + + if ($is_p) { + $value = "

    $value

    "; + } + $grafs[$key] = $value; + } + + # Join grafs in one text, then unhash HTML tags. + $text = implode("\n\n", $grafs); + + # Finish by removing any tag hashes still present in $text. + $text = $this->unhash($text); + + return $text; + } + + + ### Footnotes + + public function stripFootnotes($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [^id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1 + [ ]* + \n? # maybe *one* newline + ( # text = $2 (no blank lines allowed) + (?: + .+ # actual text + | + \n # newlines but + (?!\[\^.+?\]:\s)# negative lookahead for footnote marker. + (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed + # by non-indented content + )* + ) + }xm', + array(&$this, '_stripFootnotes_callback'), + $text); + return $text; + } + public function _stripFootnotes_callback($matches) { + $note_id = $this->fn_id_prefix . $matches[1]; + $this->footnotes[$note_id] = $this->outdent($matches[2]); + return ''; # String that will replace the block + } + + + public function doFootnotes($text) { + # + # Replace footnote references in $text [^id] with a special text-token + # which will be replaced by the actual footnote marker in appendFootnotes. + # + if (!$this->in_anchor) { + $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); + } + return $text; + } + + + public function appendFootnotes($text) { + # + # Append footnote list to text. + # + $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $text); + + if (!empty($this->footnotes_ordered)) { + $text .= "\n\n"; + $text .= "
    \n"; + $text .= "
    \n"; + $text .= "
      \n\n"; + + $attr = " rev=\"footnote\""; + if ($this->fn_backlink_class != "") { + $class = $this->fn_backlink_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_backlink_title != "") { + $title = $this->fn_backlink_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + $num = 0; + + while (!empty($this->footnotes_ordered)) { + $footnote = reset($this->footnotes_ordered); + $note_id = key($this->footnotes_ordered); + unset($this->footnotes_ordered[$note_id]); + + $footnote .= "\n"; # Need to append newline before parsing. + $footnote = $this->runBlockGamut("$footnote\n"); + $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $footnote); + + $attr = str_replace("%%", ++$num, $attr); + $note_id = $this->encodeAttribute($note_id); + + # Add backlink to last paragraph; create new paragraph if needed. + $backlink = ""; + if (preg_match('{

      $}', $footnote)) { + $footnote = substr($footnote, 0, -4) . " $backlink

      "; + } else { + $footnote .= "\n\n

      $backlink

      "; + } + + $text .= "
    1. \n"; + $text .= $footnote . "\n"; + $text .= "
    2. \n\n"; + } + + $text .= "
    \n"; + $text .= "
    "; + } + return $text; + } + public function _appendFootnotes_callback($matches) { + $node_id = $this->fn_id_prefix . $matches[1]; + + # Create footnote marker only if it has a corresponding footnote *and* + # the footnote hasn't been used by another marker. + if (isset($this->footnotes[$node_id])) { + # Transfert footnote content to the ordered list. + $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id]; + unset($this->footnotes[$node_id]); + + $num = $this->footnote_counter++; + $attr = " rel=\"footnote\""; + if ($this->fn_link_class != "") { + $class = $this->fn_link_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_link_title != "") { + $title = $this->fn_link_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + + $attr = str_replace("%%", $num, $attr); + $node_id = $this->encodeAttribute($node_id); + + return + "". + "$num". + ""; + } + + return "[^".$matches[1]."]"; + } + + + ### Abbreviations ### + + public function stripAbbreviations($text) { + # + # Strips abbreviations from text, stores titles in hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [id]*: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 + (.*) # text = $2 (no blank lines allowed) + }xm', + array(&$this, '_stripAbbreviations_callback'), + $text); + return $text; + } + public function _stripAbbreviations_callback($matches) { + $abbr_word = $matches[1]; + $abbr_desc = $matches[2]; + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + return ''; # String that will replace the block + } + + + public function doAbbreviations($text) { + # + # Find defined abbreviations in text and wrap them in elements. + # + if ($this->abbr_word_re) { + // cannot use the /x modifier because abbr_word_re may + // contain significant spaces: + $text = preg_replace_callback('{'. + '(?abbr_word_re.')'. + '(?![\w\x1A])'. + '}', + array(&$this, '_doAbbreviations_callback'), $text); + } + return $text; + } + public function _doAbbreviations_callback($matches) { + $abbr = $matches[0]; + if (isset($this->abbr_desciptions[$abbr])) { + $desc = $this->abbr_desciptions[$abbr]; + if (empty($desc)) { + return $this->hashPart("$abbr"); + } else { + $desc = $this->encodeAttribute($desc); + return $this->hashPart("$abbr"); + } + } else { + return $matches[0]; + } + } + +} diff --git a/web/framework-1.0.8/views/de/error.php b/web/framework-1.0.8/views/de/error.php new file mode 100644 index 0000000..3fea0c9 --- /dev/null +++ b/web/framework-1.0.8/views/de/error.php @@ -0,0 +1,38 @@ + + + + +Fehler <?php echo $data['code']; ?> + + + + + +

    Fehler

    +

    +

    +Beim Bearbeiten Ihrer Anfrage durch den Server ist obenstehender Fehler aufgetreten. +

    +

    +Wenn Sie der Meinung sind, dass es sich um einen Serverfehler handelt, kontaktieren Sie bitte +. +

    +

    +Vielen Dank. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/error400.php b/web/framework-1.0.8/views/de/error400.php new file mode 100644 index 0000000..f0debb1 --- /dev/null +++ b/web/framework-1.0.8/views/de/error400.php @@ -0,0 +1,34 @@ + + + + +Fehlerhafte Anfrage + + + +

    Fehlerhafte Anfrage

    +

    +

    +Der Request wurde vom Server aufgrund einer ungültigen Syntax nicht verstanden. +Bitte wiederholen Sie die Anfrage nicht unverändert. +

    +

    +Wenn Sie der Meinung sind, dass es sich um einen Serverfehler handelt, kontaktieren Sie bitte +. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/error403.php b/web/framework-1.0.8/views/de/error403.php new file mode 100644 index 0000000..14f1f5e --- /dev/null +++ b/web/framework-1.0.8/views/de/error403.php @@ -0,0 +1,33 @@ + + + + +Keine Berechtigung + + + +

    Keine Berechtigung

    +

    +

    +Sie haben nicht die nötigen Rechte, um diese Seite aufzurufen. +

    +

    +Wenn Sie der Meinung sind, dass es sich um einen Serverfehler handelt, kontaktieren Sie bitte +. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/error404.php b/web/framework-1.0.8/views/de/error404.php new file mode 100644 index 0000000..4c4987e --- /dev/null +++ b/web/framework-1.0.8/views/de/error404.php @@ -0,0 +1,34 @@ + + + + +Seite nicht gefunden + + + +

    Seite nicht gefunden

    +

    +

    +Die angeforderte URL wurde auf diesem Server nicht gefunden. +Wenn Sie die URL von Hand eingegeben haben, prüfen Sie bitte Ihre Schreibweise und versuchen Sie es dann erneut. +

    +

    +Wenn Sie der Meinung sind, dass es sich um einen Serverfehler handelt, kontaktieren Sie bitte +. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/error500.php b/web/framework-1.0.8/views/de/error500.php new file mode 100644 index 0000000..5b6fb61 --- /dev/null +++ b/web/framework-1.0.8/views/de/error500.php @@ -0,0 +1,35 @@ + + + + +Interner Serverfehler + + + + + +

    Interner Serverfehler

    +

    +

    +Beim Bearbeiten Ihrer Anfrage durch den Webserver ist ein interner Fehler aufgetreten. +Bitte kontaktieren Sie um dieses Problem zu melden. +

    +

    +Vielen Dank. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/error503.php b/web/framework-1.0.8/views/de/error503.php new file mode 100644 index 0000000..cdbbd0b --- /dev/null +++ b/web/framework-1.0.8/views/de/error503.php @@ -0,0 +1,31 @@ + + + + +Dienst nicht verfügbar + + + +

    Dienst nicht verfügbar

    +

    +Unser System wird derzeit gewartet. Bitte versuchen Sie es später noch einmal. +

    +

    +Vielen Dank. +

    +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/exception.php b/web/framework-1.0.8/views/de/exception.php new file mode 100644 index 0000000..0e40707 --- /dev/null +++ b/web/framework-1.0.8/views/de/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Beschreibung

    +

    + +

    + +

    Quelldatei

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Verfolgung des Aufrufstapels

    +
    +
    +
    +
    +
    + +
    + +
    + + diff --git a/web/framework-1.0.8/views/de/log-firebug.php b/web/framework-1.0.8/views/de/log-firebug.php new file mode 100644 index 0000000..671781a --- /dev/null +++ b/web/framework-1.0.8/views/de/log-firebug.php @@ -0,0 +1,23 @@ + diff --git a/web/framework-1.0.8/views/de/log.php b/web/framework-1.0.8/views/de/log.php new file mode 100644 index 0000000..0e6228a --- /dev/null +++ b/web/framework-1.0.8/views/de/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Anwendungsprotokoll +
    ZeitstempelStufeKategorieNachricht
    {$time}{$log[1]}{$log[2]}{$message}
    + diff --git a/web/framework-1.0.8/views/de/profile-callstack-firebug.php b/web/framework-1.0.8/views/de/profile-callstack-firebug.php new file mode 100644 index 0000000..7dc3f69 --- /dev/null +++ b/web/framework-1.0.8/views/de/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + diff --git a/web/framework-1.0.8/views/de/profile-callstack.php b/web/framework-1.0.8/views/de/profile-callstack.php new file mode 100644 index 0000000..648c9e9 --- /dev/null +++ b/web/framework-1.0.8/views/de/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Ergebnisse der Leistungsmessung anhand des Aufrufstapels +
    ProzedurZeit (s)
    {$spaces}{$proc}{$time}
    + diff --git a/web/framework-1.0.8/views/de/profile-summary-firebug.php b/web/framework-1.0.8/views/de/profile-summary-firebug.php new file mode 100644 index 0000000..07e1621 --- /dev/null +++ b/web/framework-1.0.8/views/de/profile-summary-firebug.php @@ -0,0 +1,23 @@ + diff --git a/web/framework-1.0.8/views/de/profile-summary.php b/web/framework-1.0.8/views/de/profile-summary.php new file mode 100644 index 0000000..e935dc6 --- /dev/null +++ b/web/framework-1.0.8/views/de/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Ergebnisse der Leistungsmessung + (Zeit: getExecutionTime()); ?>s, + Speicher: getMemoryUsage()/1024); ?>KB) +
    ProzedurNummerGesamt (s)Durschn. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + diff --git a/web/framework-1.0.8/views/error.php b/web/framework-1.0.8/views/error.php new file mode 100644 index 0000000..0a3b226 --- /dev/null +++ b/web/framework-1.0.8/views/error.php @@ -0,0 +1,37 @@ + + + + +Error <?php echo $data['code']; ?> + + + + + +

    Error

    +

    +

    +The above error occurred when the Web server was processing your request. +

    +

    +If you think this is a server error, please contact . +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/error400.php b/web/framework-1.0.8/views/error400.php new file mode 100644 index 0000000..c90b391 --- /dev/null +++ b/web/framework-1.0.8/views/error400.php @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    Bad Request

    +

    +

    +The request could not be understood by the server due to malformed syntax. +Please do not repeat the request without modifications. +

    +

    +If you think this is a server error, please contact . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/error403.php b/web/framework-1.0.8/views/error403.php new file mode 100644 index 0000000..c6ecf61 --- /dev/null +++ b/web/framework-1.0.8/views/error403.php @@ -0,0 +1,32 @@ + + + + +Unauthorized + + + +

    Unauthorized

    +

    +

    +You do not have the proper credential to access this page. +

    +

    +If you think this is a server error, please contact . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/error404.php b/web/framework-1.0.8/views/error404.php new file mode 100644 index 0000000..f6a761d --- /dev/null +++ b/web/framework-1.0.8/views/error404.php @@ -0,0 +1,33 @@ + + + + +Page Not Found + + + +

    Page Not Found

    +

    +

    +The requested URL was not found on this server. +If you entered the URL manually please check your spelling and try again. +

    +

    +If you think this is a server error, please contact . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/error500.php b/web/framework-1.0.8/views/error500.php new file mode 100644 index 0000000..e73011c --- /dev/null +++ b/web/framework-1.0.8/views/error500.php @@ -0,0 +1,35 @@ + + + + +Internal Server Error + + + + + +

    Internal Server Error

    +

    +

    +An internal error occurred while the Web server was processing your request. +Please contact to report this problem. +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/error503.php b/web/framework-1.0.8/views/error503.php new file mode 100644 index 0000000..4a16e75 --- /dev/null +++ b/web/framework-1.0.8/views/error503.php @@ -0,0 +1,31 @@ + + + + +Service Unavailable + + + +

    Service Unavailable

    +

    +Our system is currently under maintenance. Please come back later. +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error.php b/web/framework-1.0.8/views/es/error.php new file mode 100644 index 0000000..5edba86 --- /dev/null +++ b/web/framework-1.0.8/views/es/error.php @@ -0,0 +1,37 @@ + + + + +Error <?php echo $data['code']; ?> + + + + + +

    Error

    +

    +

    +El error mencionado ocurrió cuando el Servidor Web estaba procesando su solicitud. +

    +

    +Si piensa que este es un error del servidor, por favor contacte a . +

    +

    +Gracias. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error400.php b/web/framework-1.0.8/views/es/error400.php new file mode 100644 index 0000000..99346e4 --- /dev/null +++ b/web/framework-1.0.8/views/es/error400.php @@ -0,0 +1,33 @@ + + + + +Solicitud Incorrecta + + + +

    Solicitud incorrecta

    +

    +

    +La solicitud no pudo ser entendida por el servidor por un error de sintaxis. +Por favor, no intente realizar esta solicitud sin realizar modificaciones. +

    +

    +Si piensa que éste es un error del servidor, por favor contacte a . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error403.php b/web/framework-1.0.8/views/es/error403.php new file mode 100644 index 0000000..1e7627a --- /dev/null +++ b/web/framework-1.0.8/views/es/error403.php @@ -0,0 +1,32 @@ + + + + +No Autorizado + + + +

    No Autorizado

    +

    +

    +No tiene los permisos necesarios para acceder a esta página. +

    +

    +Si piensa que este es un error del servidor, por favor contacte a . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error404.php b/web/framework-1.0.8/views/es/error404.php new file mode 100644 index 0000000..34dd77b --- /dev/null +++ b/web/framework-1.0.8/views/es/error404.php @@ -0,0 +1,33 @@ + + + + +Página no encontrada + + + +

    Página no encontrada

    +

    +

    +La URL solicitada no fue encontrada en este servidor. +Si ingresó la dirección manualmente, por favor verifique que haya sido bien digitada e intente nuevamente. +

    +

    +Si piensa que este es un error del servidor, por favor contacte a . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error500.php b/web/framework-1.0.8/views/es/error500.php new file mode 100644 index 0000000..e73011c --- /dev/null +++ b/web/framework-1.0.8/views/es/error500.php @@ -0,0 +1,35 @@ + + + + +Internal Server Error + + + + + +

    Internal Server Error

    +

    +

    +An internal error occurred while the Web server was processing your request. +Please contact to report this problem. +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/error503.php b/web/framework-1.0.8/views/es/error503.php new file mode 100644 index 0000000..f0e401c --- /dev/null +++ b/web/framework-1.0.8/views/es/error503.php @@ -0,0 +1,31 @@ + + + + +Servicio No Disponible + + + +

    Servicio No Disponible

    +

    +Nuestro sistema está actualmente en mantenimiento. Por favor regrese más tarde. +

    +

    +Gracias. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/exception.php b/web/framework-1.0.8/views/es/exception.php new file mode 100644 index 0000000..5585414 --- /dev/null +++ b/web/framework-1.0.8/views/es/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Descripción

    +

    + +

    + +

    Archivo Fuente:

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stack Trace

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/log-firebug.php b/web/framework-1.0.8/views/es/log-firebug.php new file mode 100644 index 0000000..d3ad7e2 --- /dev/null +++ b/web/framework-1.0.8/views/es/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/log.php b/web/framework-1.0.8/views/es/log.php new file mode 100644 index 0000000..455a810 --- /dev/null +++ b/web/framework-1.0.8/views/es/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message='
    '.CHtml::encode(wordwrap($log[0])).'
    '; + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Application Log +
    TimestampLevelCategoryMessage
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/profile-callstack-firebug.php b/web/framework-1.0.8/views/es/profile-callstack-firebug.php new file mode 100644 index 0000000..0585b4f --- /dev/null +++ b/web/framework-1.0.8/views/es/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/profile-callstack.php b/web/framework-1.0.8/views/es/profile-callstack.php new file mode 100644 index 0000000..375f760 --- /dev/null +++ b/web/framework-1.0.8/views/es/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack Report +
    ProcedimientoTiempo (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/es/profile-summary-firebug.php b/web/framework-1.0.8/views/es/profile-summary-firebug.php new file mode 100644 index 0000000..0306b71 --- /dev/null +++ b/web/framework-1.0.8/views/es/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/es/profile-summary.php b/web/framework-1.0.8/views/es/profile-summary.php new file mode 100644 index 0000000..33746c0 --- /dev/null +++ b/web/framework-1.0.8/views/es/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling Summary Report + (Tiempo: getExecutionTime()); ?>s, + Memoria: getMemoryUsage()/1024); ?>KB) +
    ProcedimientoCuentaTotal (es)Promedio (s)Min. (s)Máx. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/exception.php b/web/framework-1.0.8/views/exception.php new file mode 100644 index 0000000..6a04674 --- /dev/null +++ b/web/framework-1.0.8/views/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Description

    +

    + +

    + +

    Source File

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stack Trace

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error.php b/web/framework-1.0.8/views/fr/error.php new file mode 100644 index 0000000..60f1dec --- /dev/null +++ b/web/framework-1.0.8/views/fr/error.php @@ -0,0 +1,37 @@ + + + + +Erreur <?php echo $data['code']; ?> + + + + + +

    Erreur

    +

    +

    +L'erreur ci-dessus est apparue pendant que le serveur Web traitait votre requête. +

    +

    +Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter . +

    +

    +Merci. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error400.php b/web/framework-1.0.8/views/fr/error400.php new file mode 100644 index 0000000..bfab1f7 --- /dev/null +++ b/web/framework-1.0.8/views/fr/error400.php @@ -0,0 +1,33 @@ + + + + +Demande incorrecte + + + +

    Demande Incorrecte

    +

    +

    +La demande n'a pas pu être interprétée par le serveur, due à une mauvaise syntaxe. +Merci de ne pas répeter la requete sans modifications. +

    +

    +Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error403.php b/web/framework-1.0.8/views/fr/error403.php new file mode 100644 index 0000000..c110183 --- /dev/null +++ b/web/framework-1.0.8/views/fr/error403.php @@ -0,0 +1,32 @@ + + + + +Accès interdit + + + +

    Accès interdit

    +

    +

    +Vous n'avez pas les autorisations nécessaires pour accéder à cette page. +

    +

    +Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error404.php b/web/framework-1.0.8/views/fr/error404.php new file mode 100644 index 0000000..624ead7 --- /dev/null +++ b/web/framework-1.0.8/views/fr/error404.php @@ -0,0 +1,34 @@ + + + + +Page Non trouvée + + + +

    Page Non trouvée

    +

    +

    +L'URL demandée n'existe pas sur ce serveur. +If you entered the URL manually please check your spelling and try again. +Si vous avez saisi l'URL manuellement, vérifiez la, et réessayez. +

    +

    +Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error500.php b/web/framework-1.0.8/views/fr/error500.php new file mode 100644 index 0000000..9aa175b --- /dev/null +++ b/web/framework-1.0.8/views/fr/error500.php @@ -0,0 +1,35 @@ + + + + +Erreur interne du serveur + + + + + +

    Erreur interne du serveur

    +

    +

    +Une erreur interne est apparue lorsque le serveur web traitait votre requete. +Veuillez contacter pour signaler ce problème. +

    +

    +Merci. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/error503.php b/web/framework-1.0.8/views/fr/error503.php new file mode 100644 index 0000000..d3e22ef --- /dev/null +++ b/web/framework-1.0.8/views/fr/error503.php @@ -0,0 +1,31 @@ + + + + +Service non disponible + + + +

    Service non disponible

    +

    +Notre système est momentanément indisponible. Merci de réessayer plus tard. +

    +

    +Merci. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/exception.php b/web/framework-1.0.8/views/fr/exception.php new file mode 100644 index 0000000..5651536 --- /dev/null +++ b/web/framework-1.0.8/views/fr/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Description

    +

    + +

    + +

    Fichier Source

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Pile d'exécution

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/log-firebug.php b/web/framework-1.0.8/views/fr/log-firebug.php new file mode 100644 index 0000000..fed41d3 --- /dev/null +++ b/web/framework-1.0.8/views/fr/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/log.php b/web/framework-1.0.8/views/fr/log.php new file mode 100644 index 0000000..1cc0352 --- /dev/null +++ b/web/framework-1.0.8/views/fr/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Journal d'application +
    HeureNiveauCategorieMessage
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/profile-callstack-firebug.php b/web/framework-1.0.8/views/fr/profile-callstack-firebug.php new file mode 100644 index 0000000..c676c07 --- /dev/null +++ b/web/framework-1.0.8/views/fr/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/profile-callstack.php b/web/framework-1.0.8/views/fr/profile-callstack.php new file mode 100644 index 0000000..1b88cb6 --- /dev/null +++ b/web/framework-1.0.8/views/fr/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack Report +
    ProcedureTime (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/fr/profile-summary-firebug.php b/web/framework-1.0.8/views/fr/profile-summary-firebug.php new file mode 100644 index 0000000..6e823b9 --- /dev/null +++ b/web/framework-1.0.8/views/fr/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/fr/profile-summary.php b/web/framework-1.0.8/views/fr/profile-summary.php new file mode 100644 index 0000000..ae9bfd3 --- /dev/null +++ b/web/framework-1.0.8/views/fr/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling Summary Report + (Time: getExecutionTime()); ?>s, + Memory: getMemoryUsage()/1024); ?>KB) +
    ProcedureCountTotal (s)Avg. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error.php b/web/framework-1.0.8/views/he/error.php new file mode 100644 index 0000000..1e6c1f6 --- /dev/null +++ b/web/framework-1.0.8/views/he/error.php @@ -0,0 +1,38 @@ + + + + +שגיאה <?php echo $data['code']; ?> + + + + + +

    שגיאה

    +

    +

    +השגיאה המוצגת למעלה התרחשה בעת שהשרת ניסה לעבד את הבקשה שלך. +

    +

    +אם הינך חושב שזוהי בעית שרת אנא צור קשר עם, . +

    +

    +תודה. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error400.php b/web/framework-1.0.8/views/he/error400.php new file mode 100644 index 0000000..73d8a1e --- /dev/null +++ b/web/framework-1.0.8/views/he/error400.php @@ -0,0 +1,34 @@ + + + + +בקשה לא תקינה + + + +

    בקשה שגויה

    +

    +

    +הבקשה לא עובדה על ידי השרת בצורה נכונה כתוצאה מתחביר לא תקין. +במידה וישנן טעויות תחביר בבקשה יש לתקן אותן לפני טעינת עמוד זה מחדש. +

    +

    +אם הינך חושב שזוהי שגיאת שרת אנא צור קשר עם, . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error403.php b/web/framework-1.0.8/views/he/error403.php new file mode 100644 index 0000000..1a1fe2a --- /dev/null +++ b/web/framework-1.0.8/views/he/error403.php @@ -0,0 +1,33 @@ + + + + +לא מורשה + + + +

    אין גישה

    +

    +

    +אין לך מספיק הרשאות כדי לגשת לעמוד זה +

    +

    +אם הינך חושב שזוהי טעות או בעיה בשרת אנא צור קשר עם, . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error404.php b/web/framework-1.0.8/views/he/error404.php new file mode 100644 index 0000000..d7cbcb8 --- /dev/null +++ b/web/framework-1.0.8/views/he/error404.php @@ -0,0 +1,34 @@ + + + + +עמוד לא קיים + + + +

    עמוד לא קיים

    +

    +

    +העמוד שחיפשת לא קיים או לא נמצא. +אנא וודא שהזנת את הקישור בצורה הנכונה. +

    +

    +אם הינך חושב שזוהי בעיה בשרת אנא צור קשר עם, . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error500.php b/web/framework-1.0.8/views/he/error500.php new file mode 100644 index 0000000..68223f3 --- /dev/null +++ b/web/framework-1.0.8/views/he/error500.php @@ -0,0 +1,36 @@ + + + + +שגיאת שרת פנימית + + + + + +

    שגיאת שרת פנימית

    +

    +

    +התרחשה שגיאת שרת פנימית בזמן ביצוע הבקשה. +אנא צור קשר עם כדי לדווח על תקלה זו. +

    +

    +תודה. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/error503.php b/web/framework-1.0.8/views/he/error503.php new file mode 100644 index 0000000..52a1c17 --- /dev/null +++ b/web/framework-1.0.8/views/he/error503.php @@ -0,0 +1,32 @@ + + + + +לא פעיל + + + +

    השירות לא פעיל כרגע

    +

    +המערכת שלנו עוברת תהליך שדרוג. אנא נסה שוב מאוחר יותר. +

    +

    +תודה. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/exception.php b/web/framework-1.0.8/views/he/exception.php new file mode 100644 index 0000000..44a656f --- /dev/null +++ b/web/framework-1.0.8/views/he/exception.php @@ -0,0 +1,75 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    תיאור

    +

    + +

    + +

    קובץ מקור

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    מעקב אחר הפעולות

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/log-firebug.php b/web/framework-1.0.8/views/he/log-firebug.php new file mode 100644 index 0000000..bc622df --- /dev/null +++ b/web/framework-1.0.8/views/he/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/log.php b/web/framework-1.0.8/views/he/log.php new file mode 100644 index 0000000..1142655 --- /dev/null +++ b/web/framework-1.0.8/views/he/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + רשומות האפליקציה +
    זמןרמהקטגוריההודעה
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/profile-callstack-firebug.php b/web/framework-1.0.8/views/he/profile-callstack-firebug.php new file mode 100644 index 0000000..901cf49 --- /dev/null +++ b/web/framework-1.0.8/views/he/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/profile-callstack.php b/web/framework-1.0.8/views/he/profile-callstack.php new file mode 100644 index 0000000..51d6aad --- /dev/null +++ b/web/framework-1.0.8/views/he/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + דוח ניתוח פרופילים +
    פרוצדורהזמן
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/he/profile-summary-firebug.php b/web/framework-1.0.8/views/he/profile-summary-firebug.php new file mode 100644 index 0000000..ed04a88 --- /dev/null +++ b/web/framework-1.0.8/views/he/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/he/profile-summary.php b/web/framework-1.0.8/views/he/profile-summary.php new file mode 100644 index 0000000..96808da --- /dev/null +++ b/web/framework-1.0.8/views/he/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + תקציר דוח פרופילים + (זמן: getExecutionTime()); ?>, + זכרון: getMemoryUsage()/1024); ?> קילובייט) +
    פרוצדורהכמותסך הכלממוצעמינימוםמקסימום
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error.php b/web/framework-1.0.8/views/id/error.php new file mode 100644 index 0000000..7b0d669 --- /dev/null +++ b/web/framework-1.0.8/views/id/error.php @@ -0,0 +1,37 @@ + + + + +Kesalahan <?php echo $data['code']; ?> + + + + + +

    Error

    +

    +

    +Kesalahan di atas terjadi saat server Web memproses permintaan Anda. +

    +

    +Jika Anda pikir ini adalah kesalahan server, silahkan hubungi . +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error400.php b/web/framework-1.0.8/views/id/error400.php new file mode 100644 index 0000000..f1b8c42 --- /dev/null +++ b/web/framework-1.0.8/views/id/error400.php @@ -0,0 +1,33 @@ + + + + +Permintaan Salah + + + +

    Permintaan Salah

    +

    +

    +Permintaan tidak bisa dimengerti oleh server karena salah pembentukan sintaks. +Harap tidak mengulang permintaan tanpa mengubahnya. +

    +

    +Jika Anda pikir ini adalah kesalahan server, silahkan hubungi . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error403.php b/web/framework-1.0.8/views/id/error403.php new file mode 100644 index 0000000..e4e1614 --- /dev/null +++ b/web/framework-1.0.8/views/id/error403.php @@ -0,0 +1,32 @@ + + + + +Tidak berhak + + + +

    Tidak berhak

    +

    +

    +Anda tidak memiliki kredensial yang benar untuk mengakses halaman ini. +

    +

    +Jika Anda pikir ini adalah kesalahan server, silahkan hubungi . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error404.php b/web/framework-1.0.8/views/id/error404.php new file mode 100644 index 0000000..a3acd25 --- /dev/null +++ b/web/framework-1.0.8/views/id/error404.php @@ -0,0 +1,33 @@ + + + + +Halaman Tidak Ditemukan + + + +

    Halaman Tidak Ditemukan

    +

    +

    +URL halaman yang diminta tidak ditemukan pada server. +Jika Anda memasukkan URL secara manual, silahkan periksa ejaan Anda dan coba lagi. +

    +

    +Jika Anda pikir ini adalah kesalahan server, silahkan hubungi . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error500.php b/web/framework-1.0.8/views/id/error500.php new file mode 100644 index 0000000..4629e84 --- /dev/null +++ b/web/framework-1.0.8/views/id/error500.php @@ -0,0 +1,35 @@ + + + + +Kesalahan Internal Server + + + + + +

    Kesalahan Internal Server

    +

    +

    +Kesalahan internal terjadi saat server Web memproses permintaan Anda. +Silahkan hubungi untuk melaporkan masalah ini. +

    +

    +Terima kasih. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/error503.php b/web/framework-1.0.8/views/id/error503.php new file mode 100644 index 0000000..63a8d8a --- /dev/null +++ b/web/framework-1.0.8/views/id/error503.php @@ -0,0 +1,31 @@ + + + + +Layanan Tidak Tersedia + + + +

    Layanan Tidak Tersedia

    +

    +Sistem kami saat ini dalam pemeliharaan. Silahkan kembali lagi nanti. +

    +

    +Terima kasih. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/exception.php b/web/framework-1.0.8/views/id/exception.php new file mode 100644 index 0000000..661f5f2 --- /dev/null +++ b/web/framework-1.0.8/views/id/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Deskripsi

    +

    + +

    + +

    File Sumber

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Jejak Stack

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/log-firebug.php b/web/framework-1.0.8/views/id/log-firebug.php new file mode 100644 index 0000000..a04d7bf --- /dev/null +++ b/web/framework-1.0.8/views/id/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/log.php b/web/framework-1.0.8/views/id/log.php new file mode 100644 index 0000000..530df3c --- /dev/null +++ b/web/framework-1.0.8/views/id/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Log Aplikasi +
    WaktuTingkatKategoriPesan
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/profile-callstack-firebug.php b/web/framework-1.0.8/views/id/profile-callstack-firebug.php new file mode 100644 index 0000000..57c503f --- /dev/null +++ b/web/framework-1.0.8/views/id/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/profile-callstack.php b/web/framework-1.0.8/views/id/profile-callstack.php new file mode 100644 index 0000000..35b6f6b --- /dev/null +++ b/web/framework-1.0.8/views/id/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Laporan Profil Callstack +
    ProsedurJam
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/id/profile-summary-firebug.php b/web/framework-1.0.8/views/id/profile-summary-firebug.php new file mode 100644 index 0000000..3af54b4 --- /dev/null +++ b/web/framework-1.0.8/views/id/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/id/profile-summary.php b/web/framework-1.0.8/views/id/profile-summary.php new file mode 100644 index 0000000..11e9ebd --- /dev/null +++ b/web/framework-1.0.8/views/id/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Laporan Ringkasan Profil + (Waktu: getExecutionTime()); ?>s, + Memori: getMemoryUsage()/1024); ?>KB) +
    ProsedurJumlahTotalRata-RataMin.Maks.
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error.php b/web/framework-1.0.8/views/ja/error.php new file mode 100644 index 0000000..a2cf24e --- /dev/null +++ b/web/framework-1.0.8/views/ja/error.php @@ -0,0 +1,37 @@ + + + + +Error <?php echo $data['code']; ?> + + + + + +

    エラー

    +

    +

    +リクエストを処理中にウエブサーバが上記エラーを検出しました。 +

    +

    +もしこれがサーバのエラーとお考えの場合は担当()に連絡してください。 +

    +

    +ありがとうございます。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error400.php b/web/framework-1.0.8/views/ja/error400.php new file mode 100644 index 0000000..de4d553 --- /dev/null +++ b/web/framework-1.0.8/views/ja/error400.php @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    不正リクエスト

    +

    +

    +不正な文法が検出されたため、サーバはリクエストを処理できませんでした。 +修正しないうちは同じリクエストを発行しないでください。 +

    +

    +もしこれがサーバのエラーとお考えの場合は担当()に連絡してください。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error403.php b/web/framework-1.0.8/views/ja/error403.php new file mode 100644 index 0000000..75a975c --- /dev/null +++ b/web/framework-1.0.8/views/ja/error403.php @@ -0,0 +1,32 @@ + + + + +Unauthorized + + + +

    未認証

    +

    +

    +あなたはこのページをアクセスする許可を受けていません。 +

    +

    +もしこれがサーバのエラーとお考えの場合は担当()に連絡してください。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error404.php b/web/framework-1.0.8/views/ja/error404.php new file mode 100644 index 0000000..23db2c5 --- /dev/null +++ b/web/framework-1.0.8/views/ja/error404.php @@ -0,0 +1,33 @@ + + + + +Page Not Found + + + +

    ページが見つからない

    +

    +

    +サーバにはリクエストされたURLは存在しません。もしURLを手で入力した場合には、 +スペルをチェックしてから再度トライしてください。 +

    +

    +もしこれがサーバのエラーとお考えの場合は担当()に連絡してください。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error500.php b/web/framework-1.0.8/views/ja/error500.php new file mode 100644 index 0000000..7cc2a7d --- /dev/null +++ b/web/framework-1.0.8/views/ja/error500.php @@ -0,0 +1,35 @@ + + + + +Internal Server Error + + + + + +

    サーバ内部エラー

    +

    +

    +ウエブサーバがリクエストを処理している途中にサーバの内部エラーが起きました。 +担当()にこの問題を連絡してください。 +

    +

    +ありがとうございます。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/error503.php b/web/framework-1.0.8/views/ja/error503.php new file mode 100644 index 0000000..3be6aed --- /dev/null +++ b/web/framework-1.0.8/views/ja/error503.php @@ -0,0 +1,31 @@ + + + + +Service Unavailable + + + +

    サービスが提供されません

    +

    +システムは現在メンテナンス中です。また後で来てくださるようお願いします。 +

    +

    +ありがとうございます。 +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/exception.php b/web/framework-1.0.8/views/ja/exception.php new file mode 100644 index 0000000..8c267e2 --- /dev/null +++ b/web/framework-1.0.8/views/ja/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    説明

    +

    + +

    + +

    ソースファイル

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    スタックトレース

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/log-firebug.php b/web/framework-1.0.8/views/ja/log-firebug.php new file mode 100644 index 0000000..40ac663 --- /dev/null +++ b/web/framework-1.0.8/views/ja/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/log.php b/web/framework-1.0.8/views/ja/log.php new file mode 100644 index 0000000..83a18bb --- /dev/null +++ b/web/framework-1.0.8/views/ja/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + アプリケーションログ +
    タイムスタンプレベルカテゴリメッセージ
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/profile-callstack-firebug.php b/web/framework-1.0.8/views/ja/profile-callstack-firebug.php new file mode 100644 index 0000000..0585b4f --- /dev/null +++ b/web/framework-1.0.8/views/ja/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/profile-callstack.php b/web/framework-1.0.8/views/ja/profile-callstack.php new file mode 100644 index 0000000..b213283 --- /dev/null +++ b/web/framework-1.0.8/views/ja/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + プロファイリング コールスタック レポート +
    プロシージャ時間 (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ja/profile-summary-firebug.php b/web/framework-1.0.8/views/ja/profile-summary-firebug.php new file mode 100644 index 0000000..03e44ac --- /dev/null +++ b/web/framework-1.0.8/views/ja/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/ja/profile-summary.php b/web/framework-1.0.8/views/ja/profile-summary.php new file mode 100644 index 0000000..49ce52f --- /dev/null +++ b/web/framework-1.0.8/views/ja/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + プロファイリング概要レポート + (時間: getExecutionTime()); ?>s, + メモリ: getMemoryUsage()/1024); ?>KB) +
    プロシージャ回数合計 (s)平均 (s)最小 (s)最大 (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/log-firebug.php b/web/framework-1.0.8/views/log-firebug.php new file mode 100644 index 0000000..40ac663 --- /dev/null +++ b/web/framework-1.0.8/views/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/log.php b/web/framework-1.0.8/views/log.php new file mode 100644 index 0000000..455a810 --- /dev/null +++ b/web/framework-1.0.8/views/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message='
    '.CHtml::encode(wordwrap($log[0])).'
    '; + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Application Log +
    TimestampLevelCategoryMessage
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error.php b/web/framework-1.0.8/views/nl/error.php new file mode 100644 index 0000000..1543550 --- /dev/null +++ b/web/framework-1.0.8/views/nl/error.php @@ -0,0 +1,25 @@ + + + + + Fout <?php echo $data['code']; ?> + + + + +

    Fout

    +

    +

    De bovenstaande fout trad op toen deze server uw vraag probeerde te verwerken.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error400.php b/web/framework-1.0.8/views/nl/error400.php new file mode 100644 index 0000000..08e9332 --- /dev/null +++ b/web/framework-1.0.8/views/nl/error400.php @@ -0,0 +1,24 @@ + + + + + Slechte vraag + + + +

    Slechte vraag

    +

    +

    Uw browser (of proxy) stuurde een vraag die deze server niet kon begrijpen door een slechte syntax. Herhaal a.u.b. deze vraag niet zonder correcties.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error403.php b/web/framework-1.0.8/views/nl/error403.php new file mode 100644 index 0000000..a497cd4 --- /dev/null +++ b/web/framework-1.0.8/views/nl/error403.php @@ -0,0 +1,24 @@ + + + + + Toegang verboden + + + +

    Toegang verboden

    +

    +

    U hebt niet de toestemming om toegang te krijgen tot het gevraagde object. Deze is ofwel beveiligd tegen lezen of onleesbaar door de server.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error404.php b/web/framework-1.0.8/views/nl/error404.php new file mode 100644 index 0000000..af0f4ab --- /dev/null +++ b/web/framework-1.0.8/views/nl/error404.php @@ -0,0 +1,24 @@ + + + + + Object niet gevonden + + + +

    Object niet gevonden

    +

    +

    De gevraagde URL was niet gevonden op deze server. Indien u de URL handmatig hebt ingevuld, gelieve uw spelling te controleren en probeer opnieuw.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error500.php b/web/framework-1.0.8/views/nl/error500.php new file mode 100644 index 0000000..b863632 --- /dev/null +++ b/web/framework-1.0.8/views/nl/error500.php @@ -0,0 +1,24 @@ + + + + + Server fout + + + +

    Server fout

    +

    +

    De server kreeg een interne fout en kon uw vraag niet beantwoorden.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/error503.php b/web/framework-1.0.8/views/nl/error503.php new file mode 100644 index 0000000..53b1b07 --- /dev/null +++ b/web/framework-1.0.8/views/nl/error503.php @@ -0,0 +1,24 @@ + + + + + Dienst niet beschikbaar + + + +

    Dienst niet beschikbaar

    +

    +

    De server kan tijdelijk uw vraag niet verwerken door onderhoud of problemen met de capaciteit van de server. Gelieve later nog eens opnieuw te proberen.

    +

    Als u van mening bent dat dit een server fout is, neem dan a.u.b. contact op met de

    +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/exception.php b/web/framework-1.0.8/views/nl/exception.php new file mode 100644 index 0000000..b65c57a --- /dev/null +++ b/web/framework-1.0.8/views/nl/exception.php @@ -0,0 +1,62 @@ + + + + + <?php echo CHtml::encode($data['type']); ?> + + + + +

    + +

    Omschrijving

    +

    + +

    Bronbestand

    +

    + +
    +
    +        $code) {
    +                if($line!==$data['line'])
    +                    echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +                else {
    +                    echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } + } + ?> +
    +
    + +

    Stack Trace

    +
    +
    +        
    +        
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/log-firebug.php b/web/framework-1.0.8/views/nl/log-firebug.php new file mode 100644 index 0000000..305738e --- /dev/null +++ b/web/framework-1.0.8/views/nl/log-firebug.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/log.php b/web/framework-1.0.8/views/nl/log.php new file mode 100644 index 0000000..240089f --- /dev/null +++ b/web/framework-1.0.8/views/nl/log.php @@ -0,0 +1,29 @@ + + + + + + + + + + + +$log) { + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    Applicatie Logboek
    TijdNiveauCategoreBericht
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/profile-callstack-firebug.php b/web/framework-1.0.8/views/nl/profile-callstack-firebug.php new file mode 100644 index 0000000..fc784db --- /dev/null +++ b/web/framework-1.0.8/views/nl/profile-callstack-firebug.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/profile-callstack.php b/web/framework-1.0.8/views/nl/profile-callstack.php new file mode 100644 index 0000000..0e051f0 --- /dev/null +++ b/web/framework-1.0.8/views/nl/profile-callstack.php @@ -0,0 +1,29 @@ + + + + + + + + + +$entry) { + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack Rapport +
    ProcedureTijd (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/profile-summary-firebug.php b/web/framework-1.0.8/views/nl/profile-summary-firebug.php new file mode 100644 index 0000000..12d855a --- /dev/null +++ b/web/framework-1.0.8/views/nl/profile-summary-firebug.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/nl/profile-summary.php b/web/framework-1.0.8/views/nl/profile-summary.php new file mode 100644 index 0000000..a899327 --- /dev/null +++ b/web/framework-1.0.8/views/nl/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling Samenvatting Rapport + (Tijd: getExecutionTime()); ?>s, + Geheugen: getMemoryUsage()/1024); ?>KB) +
    ProcedureAantalTotaal (s)Gem. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error.php b/web/framework-1.0.8/views/no/error.php new file mode 100644 index 0000000..c6152e8 --- /dev/null +++ b/web/framework-1.0.8/views/no/error.php @@ -0,0 +1,37 @@ + + + + +Feil <?php echo $data['code']; ?> + + + + + +

    Feil

    +

    +

    +Ovenstående feil oppsto når webserveren behandlet forespørselen din. +

    +

    +Ved mistanke om at dette er en server-feil, vennligst kontakt . +

    +

    +Takk! +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error400.php b/web/framework-1.0.8/views/no/error400.php new file mode 100644 index 0000000..b801a53 --- /dev/null +++ b/web/framework-1.0.8/views/no/error400.php @@ -0,0 +1,33 @@ + + + + +Ugyldig forespørsel + + + +

    Ugyldig forespørsel

    +

    +

    +Serveren forstod ikke syntaksen i forespørselen. Vennligst ikke repeter +forespørselen uendret. +

    +

    +Ved mistanke om at dette er en server-feil, vennligst kontakt . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error403.php b/web/framework-1.0.8/views/no/error403.php new file mode 100644 index 0000000..069dd38 --- /dev/null +++ b/web/framework-1.0.8/views/no/error403.php @@ -0,0 +1,32 @@ + + + + +Godkjenningsfeil + + + +

    Godkjenningsfeil

    +

    +

    +Du har ikke tilstrekkelig rettigheter til å nå denne siden. +

    +

    +Ved mistanke om at dette er en server-feil, vennligst kontakt . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error404.php b/web/framework-1.0.8/views/no/error404.php new file mode 100644 index 0000000..5f123d6 --- /dev/null +++ b/web/framework-1.0.8/views/no/error404.php @@ -0,0 +1,33 @@ + + + + +Siden finnes ikke + + + +

    Siden finnes ikke

    +

    +

    +Serveren finner ikke den forespurte siden. +Hvis du skrev inn URL'en manuelt vennligst sjekk om den er riktig og prøv igjen. +

    +

    +Ved mistanke om at dette er en server-feil, vennligst kontakt . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error500.php b/web/framework-1.0.8/views/no/error500.php new file mode 100644 index 0000000..2fdf5ad --- /dev/null +++ b/web/framework-1.0.8/views/no/error500.php @@ -0,0 +1,35 @@ + + + + +Intern serverfeil + + + + + +

    Intern serverfeil

    +

    +

    +Det oppstod en feil, og serveren kan ikke fullføre forespørselen. +Vennligst kontakt for å rapportere problemet. +

    +

    +Takk! +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/error503.php b/web/framework-1.0.8/views/no/error503.php new file mode 100644 index 0000000..a0dede0 --- /dev/null +++ b/web/framework-1.0.8/views/no/error503.php @@ -0,0 +1,31 @@ + + + + +Tjenesten er utilgjengelig + + + +

    Tjenesten er utilgjengelig

    +

    +Serveren er utilgjengelig for øyeblikket grunnet vedlikehold. Vennligst prøv senere. +

    +

    +Takk! +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/exception.php b/web/framework-1.0.8/views/no/exception.php new file mode 100644 index 0000000..eb29254 --- /dev/null +++ b/web/framework-1.0.8/views/no/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Beskrivelse

    +

    + +

    + +

    Kildekode

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stack Trace

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/log-firebug.php b/web/framework-1.0.8/views/no/log-firebug.php new file mode 100644 index 0000000..a01039c --- /dev/null +++ b/web/framework-1.0.8/views/no/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/log.php b/web/framework-1.0.8/views/no/log.php new file mode 100644 index 0000000..7d2c5c3 --- /dev/null +++ b/web/framework-1.0.8/views/no/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Applikasjonslogg +
    TidNivåKategoriMelding
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/profile-callstack-firebug.php b/web/framework-1.0.8/views/no/profile-callstack-firebug.php new file mode 100644 index 0000000..b1cb6e1 --- /dev/null +++ b/web/framework-1.0.8/views/no/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/profile-callstack.php b/web/framework-1.0.8/views/no/profile-callstack.php new file mode 100644 index 0000000..e42cc18 --- /dev/null +++ b/web/framework-1.0.8/views/no/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profileringsrapport +
    FunksjoneTid (sek)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/no/profile-summary-firebug.php b/web/framework-1.0.8/views/no/profile-summary-firebug.php new file mode 100644 index 0000000..8283a3e --- /dev/null +++ b/web/framework-1.0.8/views/no/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/no/profile-summary.php b/web/framework-1.0.8/views/no/profile-summary.php new file mode 100644 index 0000000..be7ed4e --- /dev/null +++ b/web/framework-1.0.8/views/no/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Resultat av profilering + (Tid: getExecutionTime()); ?>s, + Minnebruk: getMemoryUsage()/1024); ?>KB) +
    FunksjonAntallTotalt (sek)Gjennomsnitt (sek)Min (sek)Maks (sek)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/profile-callstack-firebug.php b/web/framework-1.0.8/views/profile-callstack-firebug.php new file mode 100644 index 0000000..0585b4f --- /dev/null +++ b/web/framework-1.0.8/views/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/profile-callstack.php b/web/framework-1.0.8/views/profile-callstack.php new file mode 100644 index 0000000..bf97dea --- /dev/null +++ b/web/framework-1.0.8/views/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack Report +
    ProcedureTime (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/profile-summary-firebug.php b/web/framework-1.0.8/views/profile-summary-firebug.php new file mode 100644 index 0000000..03e44ac --- /dev/null +++ b/web/framework-1.0.8/views/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/profile-summary.php b/web/framework-1.0.8/views/profile-summary.php new file mode 100644 index 0000000..a023b9f --- /dev/null +++ b/web/framework-1.0.8/views/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling Summary Report + (Time: getExecutionTime()); ?>s, + Memory: getMemoryUsage()/1024); ?>KB) +
    ProcedureCountTotal (s)Avg. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error.php b/web/framework-1.0.8/views/pt/error.php new file mode 100644 index 0000000..9f53573 --- /dev/null +++ b/web/framework-1.0.8/views/pt/error.php @@ -0,0 +1,37 @@ + + + + +Erro <?php echo $data['code']; ?> + + + + + +

    Erro

    +

    +

    +O erro acima ocorreu quando o servidor Web processava sua requisição. +

    +

    +Se você acha que isso é um erro no servidor, por favor contate . +

    +

    +Obrigado. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error400.php b/web/framework-1.0.8/views/pt/error400.php new file mode 100644 index 0000000..6f5a6e5 --- /dev/null +++ b/web/framework-1.0.8/views/pt/error400.php @@ -0,0 +1,33 @@ + + + + +Requisição Inválida + + + +

    Requisição Inválida

    +

    +

    +A requisição não pôde ser entendida pelo servidor devido a um erro de sintaxe. +Por favor, não repita a solicitação, sem modificações. +

    +

    +Se você acha que isso é um erro no servidor, por favor contate . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error403.php b/web/framework-1.0.8/views/pt/error403.php new file mode 100644 index 0000000..fa2f24b --- /dev/null +++ b/web/framework-1.0.8/views/pt/error403.php @@ -0,0 +1,32 @@ + + + + +Não Autorizado + + + +

    Não Autorizado

    +

    +

    +Você não tem as devidas credenciais para acessar esta página. +

    +

    +Se você acha que isso é um erro no servidor, por favor contate . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error404.php b/web/framework-1.0.8/views/pt/error404.php new file mode 100644 index 0000000..57caf92 --- /dev/null +++ b/web/framework-1.0.8/views/pt/error404.php @@ -0,0 +1,33 @@ + + + + +Página Não Encontrada + + + +

    Página Não Encontrada

    +

    +

    +A URL solicitada não foi encontrada neste servidor. +Se você digitou a URL manualmente verifique a ortografia e tente novamente. +

    +

    +Se você acha que isso é um erro no servidor, por favor contate . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error500.php b/web/framework-1.0.8/views/pt/error500.php new file mode 100644 index 0000000..d92d2e9 --- /dev/null +++ b/web/framework-1.0.8/views/pt/error500.php @@ -0,0 +1,35 @@ + + + + +Erro Interno Do Servidor + + + + + +

    Erro Interno Do Servidor

    +

    +

    +Ocorreu um erro interno enquanto o servidor Web processava sua requisição. +Por favor entre contate para relatar esse problema. +

    +

    +Obrigado. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/error503.php b/web/framework-1.0.8/views/pt/error503.php new file mode 100644 index 0000000..947f17a --- /dev/null +++ b/web/framework-1.0.8/views/pt/error503.php @@ -0,0 +1,31 @@ + + + + +Serviço Indisponível + + + +

    Serviço Indisponível

    +

    +Atualmente, nosso sistema encontra-se em manuntenção. Por favor, tente mais tarde. +

    +

    +Obrigado. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/exception.php b/web/framework-1.0.8/views/pt/exception.php new file mode 100644 index 0000000..da8f052 --- /dev/null +++ b/web/framework-1.0.8/views/pt/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Descrição

    +

    + +

    + +

    Arquivo Fonte

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Rastreamento da Pilha

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/log-firebug.php b/web/framework-1.0.8/views/pt/log-firebug.php new file mode 100644 index 0000000..d0abbe2 --- /dev/null +++ b/web/framework-1.0.8/views/pt/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/log.php b/web/framework-1.0.8/views/pt/log.php new file mode 100644 index 0000000..abb0478 --- /dev/null +++ b/web/framework-1.0.8/views/pt/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Log da Aplicação +
    TimestampNívelCategoriaMensagem
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/profile-callstack-firebug.php b/web/framework-1.0.8/views/pt/profile-callstack-firebug.php new file mode 100644 index 0000000..03f8b78 --- /dev/null +++ b/web/framework-1.0.8/views/pt/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/profile-callstack.php b/web/framework-1.0.8/views/pt/profile-callstack.php new file mode 100644 index 0000000..fcbf5ef --- /dev/null +++ b/web/framework-1.0.8/views/pt/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Relatorio de Profiling do Callstack +
    ProcedimentoTempo (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/pt/profile-summary-firebug.php b/web/framework-1.0.8/views/pt/profile-summary-firebug.php new file mode 100644 index 0000000..0d8920e --- /dev/null +++ b/web/framework-1.0.8/views/pt/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/pt/profile-summary.php b/web/framework-1.0.8/views/pt/profile-summary.php new file mode 100644 index 0000000..32b59a1 --- /dev/null +++ b/web/framework-1.0.8/views/pt/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Relatorio Resumido de Profiling + (Tempo: getExecutionTime()); ?>s, + Memria: getMemoryUsage()/1024); ?>KB) +
    ProcedimentoContagemTotal (s)Md. (s)Mn. (s)Mx. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error.php b/web/framework-1.0.8/views/ro/error.php new file mode 100644 index 0000000..3279f91 --- /dev/null +++ b/web/framework-1.0.8/views/ro/error.php @@ -0,0 +1,37 @@ + + + + +Eroare <?php echo $data['code']; ?> + + + + + +

    Eroarea

    +

    +

    +Eroarea de mai sus a aparut cand serverul Web procesa cererea dvs. +

    +

    +In cazul in care credeti ca este o eroare din partea serverului, va rugam contactati . +

    +

    +Va multumim. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error400.php b/web/framework-1.0.8/views/ro/error400.php new file mode 100644 index 0000000..e5dde41 --- /dev/null +++ b/web/framework-1.0.8/views/ro/error400.php @@ -0,0 +1,33 @@ + + + + +Cerere gresita + + + +

    Cerere gresita

    +

    +

    +Cererea nu este inteleasa de catre server datorita unei sintaxe necorespunzatoare. +Va rugam sa faceti modificarile necesare inainte de retrimiterea cererii. +

    +

    +In cazul in care credeti ca eroarea este de partea serverului, va rugam contactati . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error403.php b/web/framework-1.0.8/views/ro/error403.php new file mode 100644 index 0000000..d72324e --- /dev/null +++ b/web/framework-1.0.8/views/ro/error403.php @@ -0,0 +1,32 @@ + + + + +Neautorizat + + + +

    Neautorizat

    +

    +

    +Nu aveti drepturile necesare pentru a accesa aceasta pagina. +

    +

    +In cazul in care credeti ca este o eroare de partea serverului, va rugam contactati . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error404.php b/web/framework-1.0.8/views/ro/error404.php new file mode 100644 index 0000000..5fe14b9 --- /dev/null +++ b/web/framework-1.0.8/views/ro/error404.php @@ -0,0 +1,33 @@ + + + + +Pagina negasita + + + +

    Pagina negasita

    +

    +

    +URL-ul cerut nu a fost gasit pe acest server. +Va rugam sa verificati URL-ul, daca l-ati introdus manual, apoi incercati din nou. +

    +

    +In cazul in care credeti ca este o eroare de partea serverului, va rugam contactati . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error500.php b/web/framework-1.0.8/views/ro/error500.php new file mode 100644 index 0000000..27bc984 --- /dev/null +++ b/web/framework-1.0.8/views/ro/error500.php @@ -0,0 +1,35 @@ + + + + +Eroare interna server + + + + + +

    Eroare interna server

    +

    +

    +A aparut o eroare interna in timp ce serverul Web procesa cererea dvs. +Va rugam contactati pentru a raporta aceasta problema. +

    +

    +Va multumim. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/error503.php b/web/framework-1.0.8/views/ro/error503.php new file mode 100644 index 0000000..3a58b7f --- /dev/null +++ b/web/framework-1.0.8/views/ro/error503.php @@ -0,0 +1,31 @@ + + + + +Service indisponibil + + + +

    Service indisponibil

    +

    +Sistemul nostru este deocamdata intr-o perioada de mentenanta. Va rugam sa reveniti mai tarziu. +

    +

    +Va multumim. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/exception.php b/web/framework-1.0.8/views/ro/exception.php new file mode 100644 index 0000000..0e55df4 --- /dev/null +++ b/web/framework-1.0.8/views/ro/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Descriere

    +

    + +

    + +

    Fisierul sursa

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stack Trace

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/log-firebug.php b/web/framework-1.0.8/views/ro/log-firebug.php new file mode 100644 index 0000000..40ac663 --- /dev/null +++ b/web/framework-1.0.8/views/ro/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/log.php b/web/framework-1.0.8/views/ro/log.php new file mode 100644 index 0000000..8e2d2fd --- /dev/null +++ b/web/framework-1.0.8/views/ro/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Log aplicatie +
    TimestampNivelCategorieMesaj
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/profile-callstack-firebug.php b/web/framework-1.0.8/views/ro/profile-callstack-firebug.php new file mode 100644 index 0000000..0585b4f --- /dev/null +++ b/web/framework-1.0.8/views/ro/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/profile-callstack.php b/web/framework-1.0.8/views/ro/profile-callstack.php new file mode 100644 index 0000000..a840676 --- /dev/null +++ b/web/framework-1.0.8/views/ro/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack Report +
    ProceduraTimp(i)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ro/profile-summary-firebug.php b/web/framework-1.0.8/views/ro/profile-summary-firebug.php new file mode 100644 index 0000000..03e44ac --- /dev/null +++ b/web/framework-1.0.8/views/ro/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/ro/profile-summary.php b/web/framework-1.0.8/views/ro/profile-summary.php new file mode 100644 index 0000000..a023b9f --- /dev/null +++ b/web/framework-1.0.8/views/ro/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling Summary Report + (Time: getExecutionTime()); ?>s, + Memory: getMemoryUsage()/1024); ?>KB) +
    ProcedureCountTotal (s)Avg. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error.php b/web/framework-1.0.8/views/ru/error.php new file mode 100644 index 0000000..272a098 --- /dev/null +++ b/web/framework-1.0.8/views/ru/error.php @@ -0,0 +1,37 @@ + + + + +Ошибка <?php echo $data['code']; ?> + + + + + +

    Ошибка

    +

    +

    +При обработке веб-сервером вашего запроса произошла указанная выше ошибка. +

    +

    +Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

    +

    +Спасибо. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error400.php b/web/framework-1.0.8/views/ru/error400.php new file mode 100644 index 0000000..e2d7edd --- /dev/null +++ b/web/framework-1.0.8/views/ru/error400.php @@ -0,0 +1,33 @@ + + + + +Ошибка в запросе + + + +

    Ошибка в запросе

    +

    +

    +В запросе обнаружена синтаксическая ошибка, по причине которой сервер не смог обработать этот запрос. +Следует исправить ошибку, после чего повторить запрос. +

    +

    +Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error403.php b/web/framework-1.0.8/views/ru/error403.php new file mode 100644 index 0000000..cc64812 --- /dev/null +++ b/web/framework-1.0.8/views/ru/error403.php @@ -0,0 +1,32 @@ + + + + +Ошибка авторизации + + + +

    Ошибка авторизации

    +

    +

    +У вас недостаточно прав для доступа к запрошенной странице. +

    +

    +Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error404.php b/web/framework-1.0.8/views/ru/error404.php new file mode 100644 index 0000000..1dec89f --- /dev/null +++ b/web/framework-1.0.8/views/ru/error404.php @@ -0,0 +1,33 @@ + + + + +Страница не найдена + + + +

    Страница не найдена

    +

    +

    +Запрошенная страница не найдена на сервере. +Если вы ввели адрес страницы вручную, проверьте правильность его написания и повторите попытку. +

    +

    +Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error500.php b/web/framework-1.0.8/views/ru/error500.php new file mode 100644 index 0000000..178f001 --- /dev/null +++ b/web/framework-1.0.8/views/ru/error500.php @@ -0,0 +1,35 @@ + + + + +Внутренняя ошибка сервера + + + + + +

    Внутренняя ошибка сервера

    +

    +

    +В ходе обработки вашего запроса произошла внутренняя ошибка сервера. +Вы можете сообщить об ошибке по адресу . +

    +

    +Спасибо. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/error503.php b/web/framework-1.0.8/views/ru/error503.php new file mode 100644 index 0000000..06bf662 --- /dev/null +++ b/web/framework-1.0.8/views/ru/error503.php @@ -0,0 +1,31 @@ + + + + +Служба временно недоступна + + + +

    Служба временно недоступна

    +

    +В настоящий момент производится техническое обслуживание системы. Возвращайтесь позже. +

    +

    +Спасибо. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/exception.php b/web/framework-1.0.8/views/ru/exception.php new file mode 100644 index 0000000..323c5fa --- /dev/null +++ b/web/framework-1.0.8/views/ru/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Описание

    +

    + +

    + +

    Исходный код

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Содержимое стека

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/log-firebug.php b/web/framework-1.0.8/views/ru/log-firebug.php new file mode 100644 index 0000000..3d8b745 --- /dev/null +++ b/web/framework-1.0.8/views/ru/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/log.php b/web/framework-1.0.8/views/ru/log.php new file mode 100644 index 0000000..7830b47 --- /dev/null +++ b/web/framework-1.0.8/views/ru/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message='
    '.CHtml::encode(wordwrap($log[0])).'
    '; + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Журнал приложения +
    ВремяУровеньКатегорияСообщение
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/profile-callstack-firebug.php b/web/framework-1.0.8/views/ru/profile-callstack-firebug.php new file mode 100644 index 0000000..72aaf78 --- /dev/null +++ b/web/framework-1.0.8/views/ru/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/profile-callstack.php b/web/framework-1.0.8/views/ru/profile-callstack.php new file mode 100644 index 0000000..42aaf2a --- /dev/null +++ b/web/framework-1.0.8/views/ru/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Стек вызовов +
    ПроцедураВремя (с)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/ru/profile-summary-firebug.php b/web/framework-1.0.8/views/ru/profile-summary-firebug.php new file mode 100644 index 0000000..a72bc2c --- /dev/null +++ b/web/framework-1.0.8/views/ru/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/ru/profile-summary.php b/web/framework-1.0.8/views/ru/profile-summary.php new file mode 100644 index 0000000..1200cb0 --- /dev/null +++ b/web/framework-1.0.8/views/ru/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Общий отчет + (Время: getExecutionTime()); ?>с, + Память: getMemoryUsage()/1024); ?>Кб) +
    ПроцедураНомерВсего (с)Средн. (с)Мин. (с)Макс. (с)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error.php b/web/framework-1.0.8/views/sv/error.php new file mode 100644 index 0000000..a82234f --- /dev/null +++ b/web/framework-1.0.8/views/sv/error.php @@ -0,0 +1,37 @@ + + + + +Fel <?php echo $data['code']; ?> + + + + + +

    Fel

    +

    +

    +Ovanstående fel uppstod när servern behandlade Er förfrågan. +

    +

    +Vid misstanke om serverfel, kontakta . +

    +

    +Thank you. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error400.php b/web/framework-1.0.8/views/sv/error400.php new file mode 100644 index 0000000..b30045e --- /dev/null +++ b/web/framework-1.0.8/views/sv/error400.php @@ -0,0 +1,33 @@ + + + + +Felaktig förfrågan + + + +

    Felaktig förfrågan

    +

    +

    +Servern förstod inte denna förfrågan på grund av felaktig syntax. +Vänligen repetera inte denna förfrågan omodifierad. +

    +

    +Vid misstanke om serverfel, kontakta . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error403.php b/web/framework-1.0.8/views/sv/error403.php new file mode 100644 index 0000000..76e5a63 --- /dev/null +++ b/web/framework-1.0.8/views/sv/error403.php @@ -0,0 +1,33 @@ + + + + +Ej auktoriserad + + + +

    Ej auktoriserad

    +

    +

    +Otillräckliga rättigheter för åtkomst till denna sida. +

    +

    +Vid misstanke om serverfel, kontakta . + +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error404.php b/web/framework-1.0.8/views/sv/error404.php new file mode 100644 index 0000000..779f713 --- /dev/null +++ b/web/framework-1.0.8/views/sv/error404.php @@ -0,0 +1,33 @@ + + + + +Sidan hittades inte + + + +

    Sidan hittades inte

    +

    +

    +Den begärda URL:en hittades inte på denna server. +Om URL:en matades in manuellt, kontrollera stavningen och försök igen. +

    +

    +Vid misstanke om serverfel, kontakta . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error500.php b/web/framework-1.0.8/views/sv/error500.php new file mode 100644 index 0000000..0f78b57 --- /dev/null +++ b/web/framework-1.0.8/views/sv/error500.php @@ -0,0 +1,35 @@ + + + + +Internt serverfel + + + + + +

    Internt serverfel

    +

    +

    +Ett internt fel uppstod vid behandlingen av Er begäran. +Vänligen kontakta för att rapportera detta problem. +

    +

    +Tack. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/error503.php b/web/framework-1.0.8/views/sv/error503.php new file mode 100644 index 0000000..db8902f --- /dev/null +++ b/web/framework-1.0.8/views/sv/error503.php @@ -0,0 +1,31 @@ + + + + +Tjänsten ej tillgänglig + + + +

    Tjänsten ej tillgänglig

    +

    +Vårt system är för närvarande stängt pga underhåll. Vänligen kom tillbaka senare. +

    +

    +Tack. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/exception.php b/web/framework-1.0.8/views/sv/exception.php new file mode 100644 index 0000000..ad5fe91 --- /dev/null +++ b/web/framework-1.0.8/views/sv/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    Beskrivning

    +

    + +

    + +

    Källkodsfil

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stackspårning

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/log-firebug.php b/web/framework-1.0.8/views/sv/log-firebug.php new file mode 100644 index 0000000..d9edf75 --- /dev/null +++ b/web/framework-1.0.8/views/sv/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/log.php b/web/framework-1.0.8/views/sv/log.php new file mode 100644 index 0000000..52a2480 --- /dev/null +++ b/web/framework-1.0.8/views/sv/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + Applikationslogg +
    TimestampNivåKategoriMeddelande
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/profile-callstack-firebug.php b/web/framework-1.0.8/views/sv/profile-callstack-firebug.php new file mode 100644 index 0000000..ea463f5 --- /dev/null +++ b/web/framework-1.0.8/views/sv/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/profile-callstack.php b/web/framework-1.0.8/views/sv/profile-callstack.php new file mode 100644 index 0000000..ea967de --- /dev/null +++ b/web/framework-1.0.8/views/sv/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profileringsrapport - Anropsstack +
    ProcedurTid (s)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/sv/profile-summary-firebug.php b/web/framework-1.0.8/views/sv/profile-summary-firebug.php new file mode 100644 index 0000000..f28104b --- /dev/null +++ b/web/framework-1.0.8/views/sv/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/sv/profile-summary.php b/web/framework-1.0.8/views/sv/profile-summary.php new file mode 100644 index 0000000..bca34d6 --- /dev/null +++ b/web/framework-1.0.8/views/sv/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profileringsrapport - Sammanställning + (Tid: getExecutionTime()); ?>s, + Minne: getMemoryUsage()/1024); ?>KB) +
    ProcedureAntalTotal (s)Medel. (s)Min. (s)Max. (s)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error.php b/web/framework-1.0.8/views/zh_cn/error.php new file mode 100644 index 0000000..90d8095 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error.php @@ -0,0 +1,37 @@ + + + + +错误 <?php echo $data['code']; ?> + + + + + +

    错误

    +

    +

    +当网页服务器处理您的请求时, 发生以上错误. +

    +

    +如果您认为此错误是服务器造成的,请与管理员联系 . +

    +

    +谢谢. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error400.php b/web/framework-1.0.8/views/zh_cn/error400.php new file mode 100644 index 0000000..accd0ab --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error400.php @@ -0,0 +1,33 @@ + + + + +请求错误 + + + +

    请求错误

    +

    +

    +由于语法错误,服务器无法解析. +请修改请求后重试. +

    +

    +如果您认为此错误是服务器造成的,请与管理员联系 . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error403.php b/web/framework-1.0.8/views/zh_cn/error403.php new file mode 100644 index 0000000..d5002d5 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error403.php @@ -0,0 +1,32 @@ + + + + +未被授权 + + + +

    未被授权

    +

    +

    +你没有访问此页面的授权. +

    +

    +如果您认为此错误是服务器造成的,请与管理员联系 . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error404.php b/web/framework-1.0.8/views/zh_cn/error404.php new file mode 100644 index 0000000..b7edb57 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error404.php @@ -0,0 +1,33 @@ + + + + +无法找到网页 + + + +

    无法找到网页

    +

    +

    +服务器无法找到请求的 URL 所对应的网页. +如果您是手动输入URL, 请检查是否拼写无误后重试. +

    +

    +如果您认为此错误是服务器造成的,请与管理员联系 . +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error500.php b/web/framework-1.0.8/views/zh_cn/error500.php new file mode 100644 index 0000000..1427569 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error500.php @@ -0,0 +1,35 @@ + + + + +服务器内部错误 + + + + + +

    服务器内部错误

    +

    +

    +当前网页服务器在处理您的请求时,发生了一个内部错误. +请将此问题报告给服务器管理员 . +

    +

    +谢谢. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/error503.php b/web/framework-1.0.8/views/zh_cn/error503.php new file mode 100644 index 0000000..01ad475 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/error503.php @@ -0,0 +1,31 @@ + + + + +目前服务不可用 + + + +

    目前服务不可用

    +

    +系统目前正在维护中,请稍候再试. +

    +

    +对此带来的不便,深感歉意. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/exception.php b/web/framework-1.0.8/views/zh_cn/exception.php new file mode 100644 index 0000000..f981bd4 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    描述

    +

    + +

    + +

    源文件

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    堆栈追踪

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/log-firebug.php b/web/framework-1.0.8/views/zh_cn/log-firebug.php new file mode 100644 index 0000000..ab354d2 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/log.php b/web/framework-1.0.8/views/zh_cn/log.php new file mode 100644 index 0000000..8769a05 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + 程序日志 +
    时间等级类别信息
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/profile-callstack-firebug.php b/web/framework-1.0.8/views/zh_cn/profile-callstack-firebug.php new file mode 100644 index 0000000..9a232de --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/profile-callstack.php b/web/framework-1.0.8/views/zh_cn/profile-callstack.php new file mode 100644 index 0000000..0f2037b --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/profile-callstack.php @@ -0,0 +1,28 @@ + + + + + + + + + + $entry ) { + $color = ($index % 2) ? '#F5F5F5' : '#EBF8FE'; + list ( $proc, $time, $level ) = $entry; + $proc = CHtml::encode ( $proc ); + $time = sprintf ( '%0.5f', $time ); + $spaces = str_repeat ( ' ', $level * 8 ); + + echo << + + + +EOD; +} +?> +
    程序概要分析 - 堆栈调用报告
    步骤时间 (秒)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_cn/profile-summary-firebug.php b/web/framework-1.0.8/views/zh_cn/profile-summary-firebug.php new file mode 100644 index 0000000..e0b2652 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/zh_cn/profile-summary.php b/web/framework-1.0.8/views/zh_cn/profile-summary.php new file mode 100644 index 0000000..5da7f42 --- /dev/null +++ b/web/framework-1.0.8/views/zh_cn/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + 程序概要分析 - 报告概述 + (时间: getExecutionTime()); ?> 秒, + 内存: getMemoryUsage()/1024); ?>KB) +
    程序数量总计 (秒)平均 (秒)最小 (秒)最大 (秒)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error.php b/web/framework-1.0.8/views/zh_tw/error.php new file mode 100644 index 0000000..7567c3f --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error.php @@ -0,0 +1,37 @@ + + + + +Error <?php echo $data['code']; ?> + + + + + +

    錯誤

    +

    +

    +當網頁伺服器正在處理您的請求時, 發生以上的錯誤. +

    +

    +如果您認為是伺服器錯誤所造成, 請聯絡. +

    +

    +謝謝. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error400.php b/web/framework-1.0.8/views/zh_tw/error400.php new file mode 100644 index 0000000..8fc7dd0 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error400.php @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    不正確的請求

    +

    +

    +由於語法不正確, 伺服器無法了解請求的正確內容. +請修改請求後再行重試. +

    +

    +如果您認為是伺服器錯誤所造成, 請聯絡. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error403.php b/web/framework-1.0.8/views/zh_tw/error403.php new file mode 100644 index 0000000..040d42a --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error403.php @@ -0,0 +1,32 @@ + + + + +Unauthorized + + + +

    未被授權

    +

    +

    +您沒有存取此頁面的適當憑據. +

    +

    +如果您認為是伺服器錯誤所造成, 請聯絡. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error404.php b/web/framework-1.0.8/views/zh_tw/error404.php new file mode 100644 index 0000000..2b48179 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error404.php @@ -0,0 +1,33 @@ + + + + +Page Not Found + + + +

    找不到網頁

    +

    +

    +伺服器無法找到請求的 URL 對應的網頁. +如果您是手動輸入 URL, 請檢查是否有拼寫錯誤後再重試. +

    +

    +如果您認為是伺服器錯誤所造成, 請聯絡. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error500.php b/web/framework-1.0.8/views/zh_tw/error500.php new file mode 100644 index 0000000..36eb9cf --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error500.php @@ -0,0 +1,35 @@ + + + + +Internal Server Error + + + + + +

    內部的伺服器錯誤

    +

    +

    +當網頁伺服器正在處理您的請求時, 發生了一個內部錯誤. +請回報此一問題給 . +

    +

    +謝謝. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/error503.php b/web/framework-1.0.8/views/zh_tw/error503.php new file mode 100644 index 0000000..d348255 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/error503.php @@ -0,0 +1,31 @@ + + + + +Service Unavailable + + + +

    目前無法提供服務

    +

    +系統目前正在維護中. 請稍後再試. +

    +

    +謝謝. +

    +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/exception.php b/web/framework-1.0.8/views/zh_tw/exception.php new file mode 100644 index 0000000..251ac13 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

    + +

    說明

    +

    + +

    + +

    來源檔案

    +

    + +

    + +
    +
    +$code)
    +	{
    +		if($line!==$data['line'])
    +			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
    +		else
    +		{
    +			echo "
    "; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
    "; + } + } +} +?> +
    +
    + +

    Stack Trace

    +
    +
    +
    +
    +
    + +
    + +
    + + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/log-firebug.php b/web/framework-1.0.8/views/zh_tw/log-firebug.php new file mode 100644 index 0000000..9fef951 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/log.php b/web/framework-1.0.8/views/zh_tw/log.php new file mode 100644 index 0000000..493a00f --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message=CHtml::encode($log[0]); + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
    + 應用程式日誌 +
    時間戳等級分類訊息
    {$time}{$log[1]}{$log[2]}{$message}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/profile-callstack-firebug.php b/web/framework-1.0.8/views/zh_tw/profile-callstack-firebug.php new file mode 100644 index 0000000..c1506d8 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/profile-callstack.php b/web/framework-1.0.8/views/zh_tw/profile-callstack.php new file mode 100644 index 0000000..a2352c1 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
    + Profiling Callstack 報告 +
    程序時間 (秒)
    {$spaces}{$proc}{$time}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/views/zh_tw/profile-summary-firebug.php b/web/framework-1.0.8/views/zh_tw/profile-summary-firebug.php new file mode 100644 index 0000000..099c1c6 --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/web/framework-1.0.8/views/zh_tw/profile-summary.php b/web/framework-1.0.8/views/zh_tw/profile-summary.php new file mode 100644 index 0000000..c98a93c --- /dev/null +++ b/web/framework-1.0.8/views/zh_tw/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
    + Profiling 摘要報告 + (Time: getExecutionTime()); ?>s, + Memory: getMemoryUsage()/1024); ?>KB) +
    程序計數總計 (秒)平均 (秒)最小 (秒)最大 (秒)
    {$proc}{$entry[1]}{$total}{$average}{$min}{$max}
    + \ No newline at end of file diff --git a/web/framework-1.0.8/web/CAssetManager.php b/web/framework-1.0.8/web/CAssetManager.php new file mode 100644 index 0000000..882975f --- /dev/null +++ b/web/framework-1.0.8/web/CAssetManager.php @@ -0,0 +1,227 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CAssetManager is a Web application component that manages private files (called assets) and makes them accessible by Web clients. + * + * It achieves this goal by copying assets to a Web-accessible directory + * and returns the corresponding URL for accessing them. + * + * To publish an asset, simply call {@link publish()}. + * + * The Web-accessible directory holding the published files is specified + * by {@link setBasePath basePath}, which defaults to the "assets" directory + * under the directory containing the application entry script file. + * The property {@link setBaseUrl baseUrl} refers to the URL for accessing + * the {@link setBasePath basePath}. + * + * @author Qiang Xue + * @version $Id: CAssetManager.php 1261 2009-07-20 21:54:24Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CAssetManager extends CApplicationComponent +{ + /** + * Default web accessible base path for storing private files + */ + const DEFAULT_BASEPATH='assets'; + /** + * @var string base web accessible path for storing private files + */ + private $_basePath; + /** + * @var string base URL for accessing the publishing directory. + */ + private $_baseUrl; + /** + * @var array published assets + */ + private $_published=array(); + + /** + * Initializes the application component. + * This method is required by IApplicationComponent and is invoked by application. + */ + public function init() + { + parent::init(); + $request=Yii::app()->getRequest(); + if($this->getBasePath()===null) + $this->setBasePath(dirname($request->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH); + if($this->getBaseUrl()===null) + $this->setBaseUrl($request->getBaseUrl().'/'.self::DEFAULT_BASEPATH); + } + + /** + * @return string the root directory storing the published asset files + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * Sets the root directory storing published asset files. + * @param string the root directory storing published asset files + * @throws CException if the base path is invalid + */ + public function setBasePath($value) + { + if(($basePath=realpath($value))!==false && is_dir($basePath) && is_writable($basePath)) + $this->_basePath=$basePath; + else + throw new CException(Yii::t('yii','CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.', + array('{path}'=>$value))); + } + + /** + * @return string the base url that the published asset files can be accessed. + * Note, the ending slashes are stripped off. + */ + public function getBaseUrl() + { + return $this->_baseUrl; + } + + /** + * @param string the base url that the published asset files can be accessed + */ + public function setBaseUrl($value) + { + $this->_baseUrl=rtrim($value,'/'); + } + + /** + * Publishes a file or a directory. + * This method will copy the specified asset to a web accessible directory + * and return the URL for accessing the published asset. + *
      + *
    • If the asset is a file, its file modification time will be checked + * to avoid unnecessary file copying;
    • + *
    • If the asset is a directory, all files and subdirectories under it will + * be published recursively. Note, in this case the method only checks the + * existence of the target directory to avoid repetitive copying.
    • + *
    + * @param string the asset (file or directory) to be published + * @param boolean whether the published directory should be named as the hashed basename. + * If false, the name will be the hashed dirname of the path being published. + * Defaults to false. Set true if the path being published is shared among + * different extensions. + * @param integer level of recursive copying when the asset is a directory. + * Level -1 means publishing all subdirectories and files; + * Level 0 means publishing only the files DIRECTLY under the directory; + * level N means copying those directories that are within N levels. + * @return string an absolute URL to the published asset + * @throws CException if the asset to be published does not exist. + */ + public function publish($path,$hashByName=false,$level=-1) + { + if(isset($this->_published[$path])) + return $this->_published[$path]; + else if(($src=realpath($path))!==false) + { + if(is_file($src)) + { + $dir=$this->hash($hashByName ? basename($src) : dirname($src)); + $fileName=basename($src); + $dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; + $dstFile=$dstDir.DIRECTORY_SEPARATOR.$fileName; + + if(@filemtime($dstFile)<@filemtime($src)) + { + if(!is_dir($dstDir)) + { + mkdir($dstDir); + @chmod($dstDir,0777); + } + copy($src,$dstFile); + } + + return $this->_published[$path]=$this->getBaseUrl()."/$dir/$fileName"; + } + else if(is_dir($src)) + { + $dir=$this->hash($hashByName ? basename($src) : $src); + $dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; + + if(!is_dir($dstDir)) + CFileHelper::copyDirectory($src,$dstDir,array('exclude'=>array('.svn'),'level'=>$level)); + + return $this->_published[$path]=$this->getBaseUrl().'/'.$dir; + } + } + throw new CException(Yii::t('yii','The asset "{asset}" to be published does not exist.', + array('{asset}'=>$path))); + } + + /** + * Returns the published path of a file path. + * This method does not perform any publishing. It merely tells you + * if the file or directory is published, where it will go. + * @param string directory or file path being published + * @param boolean whether the published directory should be named as the hashed basename. + * If false, the name will be the hashed dirname of the path being published. + * Defaults to false. Set true if the path being published is shared among + * different extensions. + * @return string the published file path. False if the file or directory does not exist + */ + public function getPublishedPath($path,$hashByName=false) + { + if(($path=realpath($path))!==false) + { + $base=$this->getBasePath().DIRECTORY_SEPARATOR; + if(is_file($path)) + return $base . $this->hash($hashByName ? basename($path) : dirname($path)) . DIRECTORY_SEPARATOR . basename($path); + else + return $base . $this->hash($hashByName ? basename($path) : $path); + } + else + return false; + } + + /** + * Returns the URL of a published file path. + * This method does not perform any publishing. It merely tells you + * if the file path is published, what the URL will be to access it. + * @param string directory or file path being published + * @param boolean whether the published directory should be named as the hashed basename. + * If false, the name will be the hashed dirname of the path being published. + * Defaults to false. Set true if the path being published is shared among + * different extensions. + * @return string the published URL for the file or directory. False if the file or directory does not exist. + */ + public function getPublishedUrl($path,$hashByName=false) + { + if(isset($this->_published[$path])) + return $this->_published[$path]; + if(($path=realpath($path))!==false) + { + if(is_file($path)) + return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : dirname($path)).'/'.basename($path); + else + return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : $path); + } + else + return false; + } + + /** + * Generate a CRC32 hash for the directory path. Collisions are higher + * than MD5 but generates a much smaller hash string. + * @param string string to be hashed. + * @return string hashed string. + */ + protected function hash($path) + { + return sprintf('%x',crc32($path.Yii::getVersion())); + } +} diff --git a/web/framework-1.0.8/web/CBaseController.php b/web/framework-1.0.8/web/CBaseController.php new file mode 100644 index 0000000..dd6fdc7 --- /dev/null +++ b/web/framework-1.0.8/web/CBaseController.php @@ -0,0 +1,283 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CBaseController is the base class for {@link CController} and {@link CWidget}. + * + * It provides the common functionalities shared by controllers who need to render views. + * + * CBaseController also implements the support for the following features: + *
      + *
    • {@link CClipWidget Clips} : a clip is a piece of captured output that can be inserted elsewhere.
    • + *
    • {@link CWidget Widgets} : a widget is a self-contained sub-controller with its own view and model.
    • + *
    • {@link COutputCache Fragment cache} : fragment cache selectively caches a portion of the output.
    • + *
    + * + * To use a widget in a view, use the following in the view: + *
    + * $this->widget('path.to.widgetClass',array('property1'=>'value1',...));
    + * 
    + * or + *
    + * $this->beginWidget('path.to.widgetClass',array('property1'=>'value1',...));
    + * // ... display other contents here
    + * $this->endWidget();
    + * 
    + * + * To create a clip, use the following: + *
    + * $this->beginClip('clipID');
    + * // ... display the clip contents
    + * $this->endClip();
    + * 
    + * Then, in a different view or place, the captured clip can be inserted as: + *
    + * echo $this->clips['clipID'];
    + * 
    + * + * To use fragment cache, do as follows, + *
    + * if($this->beginCache('cacheID',array('property1'=>'value1',...))
    + * {
    + *     // ... display the content to be cached here
    + *    $this->endCache();
    + * }
    + * 
    + * + * @author Qiang Xue + * @version $Id: CBaseController.php 904 2009-03-31 11:51:18Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +abstract class CBaseController extends CComponent +{ + private $_widgetStack=array(); + + /** + * Returns the view script file according to the specified view name. + * This method must be implemented by child classes. + * @param string view name + * @return string the file path for the named view. False if the view cannot be found. + */ + abstract public function getViewFile($viewName); + + + /** + * Renders a view file. + * + * @param string view file path + * @param array data to be extracted and made available to the view + * @param boolean whether the rendering result should be returned instead of being echoed + * @return string the rendering result. Null if the rendering result is not required. + * @throws CException if the view file does not exist + */ + public function renderFile($viewFile,$data=null,$return=false) + { + $widgetCount=count($this->_widgetStack); + if(($renderer=Yii::app()->getViewRenderer())!==null) + $content=$renderer->renderFile($this,$viewFile,$data,$return); + else + $content=$this->renderInternal($viewFile,$data,$return); + if(count($this->_widgetStack)===$widgetCount) + return $content; + else + { + $widget=end($this->_widgetStack); + throw new CException(Yii::t('yii','{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.', + array('{controller}'=>get_class($this), '{view}'=>$viewFile, '{widget}'=>get_class($widget)))); + } + } + + /** + * Renders a view file. + * This method includes the view file as a PHP script + * and captures the display result if required. + * @param string view file + * @param array data to be extracted and made available to the view file + * @param boolean whether the rendering result should be returned as a string + * @return string the rendering result. Null if the rendering result is not required. + */ + public function renderInternal($_viewFile_,$_data_=null,$_return_=false) + { + // we use special variable names here to avoid conflict when extracting data + if(is_array($_data_)) + extract($_data_,EXTR_PREFIX_SAME,'data'); + else + $data=$_data_; + if($_return_) + { + ob_start(); + ob_implicit_flush(false); + require($_viewFile_); + return ob_get_clean(); + } + else + require($_viewFile_); + } + + /** + * Creates a widget and initializes it. + * This method first creates the specified widget instance. + * It then configures the widget's properties with the given initial values. + * At the end it calls {@link CWidget::init} to initialize the widget. + * @param string class name (can be in path alias format) + * @param array initial property values + * @return CWidget the fully initialized widget instance. + */ + public function createWidget($className,$properties=array()) + { + $className=Yii::import($className,true); + $widget=new $className($this); + foreach($properties as $name=>$value) + $widget->$name=$value; + $widget->init(); + return $widget; + } + + /** + * Creates a widget and executes it. + * @param string the widget class name or class in dot syntax (e.g. application.widgets.MyWidget) + * @param array list of initial property values for the widget (Property Name => Property Value) + * @return CWidget the widget instance. This return value has been available since version 1.0.4. + */ + public function widget($className,$properties=array()) + { + $widget=$this->createWidget($className,$properties); + $widget->run(); + return $widget; + } + + /** + * Creates a widget and executes it. + * This method is similar to {@link widget()} except that it is expecting + * a {@link endWidget()} call to end the execution. + * @param string the widget class name or class in dot syntax (e.g. application.widgets.MyWidget) + * @param array list of initial property values for the widget (Property Name => Property Value) + * @return CWidget the widget created to run + * @see endWidget + */ + public function beginWidget($className,$properties=array()) + { + $widget=$this->createWidget($className,$properties); + $this->_widgetStack[]=$widget; + return $widget; + } + + /** + * Ends the execution of the named widget. + * This method is used together with {@link beginWidget()}. + * @param string optional tag identifying the method call for debugging purpose. + * @return CWidget the widget just ended running + * @throws CException if an extra endWidget call is made + * @see beginWidget + */ + public function endWidget($id='') + { + if(($widget=array_pop($this->_widgetStack))!==null) + { + $widget->run(); + return $widget; + } + else + throw new CException(Yii::t('yii','{controller} has an extra endWidget({id}) call in its view.', + array('{controller}'=>get_class($this),'{id}'=>$id))); + } + + /** + * Begins recording a clip. + * This method is a shortcut to beginning {@link CClipWidget}. + * @param string the clip ID. + * @param array initial property values for {@link CClipWidget}. + */ + public function beginClip($id,$properties=array()) + { + $properties['id']=$id; + $this->beginWidget('CClipWidget',$properties); + } + + /** + * Ends recording a clip. + * This method is an alias to {@link endWidget}. + */ + public function endClip() + { + $this->endWidget('CClipWidget'); + } + + /** + * Begins fragment caching. + * This method will display cached content if it is availabe. + * If not, it will start caching and would expect a {@link endCache()} + * call to end the cache and save the content into cache. + * A typical usage of fragment caching is as follows, + *
    +	 * if($this->beginCache($id))
    +	 * {
    +	 *     // ...generate content here
    +	 *     $this->endCache();
    +	 * }
    +	 * 
    + * @param string a unique ID identifying the fragment to be cached. + * @param array initial property values for {@link COutputCache}. + * @return boolean whether we need to generate content for caching. False if cached version is available. + * @see endCache + */ + public function beginCache($id,$properties=array()) + { + $properties['id']=$id; + $cache=$this->beginWidget('COutputCache',$properties); + if($cache->getIsContentCached()) + { + $this->endCache(); + return false; + } + else + return true; + } + + /** + * Ends fragment caching. + * This is an alias to {@link endWidget}. + * @see beginCache + */ + public function endCache() + { + $this->endWidget('COutputCache'); + } + + /** + * Begins the rendering of content that is to be decorated by the specified view. + * @param mixed the name of the view that will be used to decorate the content. The actual view script + * is resolved via {@link getViewFile}. If this parameter is null (default), + * the default layout will be used as the decorative view. + * Note that if the current controller does not belong to + * any module, the default layout refers to the application's {@link CWebApplication::layout default layout}; + * If the controller belongs to a module, the default layout refers to the module's + * {@link CWebModule::layout default layout}. + * @param array the variables (name=>value) to be extracted and made available in the decorative view. + * This parameter has been available since version 1.0.4 + * @see beginContent + * @see CContentDecorator + */ + public function beginContent($view=null,$data=array()) + { + $this->beginWidget('CContentDecorator',array('view'=>$view, 'data'=>$data)); + } + + /** + * Ends the rendering of content. + * @see beginContent + */ + public function endContent() + { + $this->endWidget('CContentDecorator'); + } +} diff --git a/web/framework-1.0.8/web/CCacheHttpSession.php b/web/framework-1.0.8/web/CCacheHttpSession.php new file mode 100644 index 0000000..949616c --- /dev/null +++ b/web/framework-1.0.8/web/CCacheHttpSession.php @@ -0,0 +1,111 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CCacheHttpSession implements a session component using cache as storage medium. + * + * The cache being used can be any cache application component implementing {@link ICache} interface. + * The ID of the cache application component is specified via {@link cacheID}, which defaults to 'cache'. + * + * Beware, by definition cache storage are volatile, which means the data stored on them + * may be swapped out and get lost. Therefore, you must make sure the cache used by this component + * is NOT volatile. If you want to use {@link CDbCache} as storage medium, use {@link CDbHttpSession} + * is a better choice. + * + * @author Qiang Xue + * @version $Id: CCacheHttpSession.php 1136 2009-06-17 13:16:14Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CCacheHttpSession extends CHttpSession +{ + /** + * Prefix to the keys for storing cached data + */ + const CACHE_KEY_PREFIX='Yii.CCacheHttpSession.'; + /** + * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.) + */ + public $cacheID='cache'; + + /** + * @var ICache the cache component + */ + private $_cache; + + /** + * Initializes the application component. + * This method overrides the parent implementation by checking if cache is available. + */ + public function init() + { + $this->_cache=Yii::app()->getComponent($this->cacheID); + if(!($this->_cache instanceof ICache)) + throw new CException(Yii::t('yii','CCacheHttpSession.cacheID is invalid. Please make sure "{id}" refers to a valid cache application component.', + array('{id}'=>$this->cacheID))); + parent::init(); + } + + /** + * Returns a value indicating whether to use custom session storage. + * This method overrides the parent implementation and always returns true. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return true; + } + + /** + * Session read handler. + * Do not call this method directly. + * @param string session ID + * @return string the session data + */ + public function readSession($id) + { + $data=$this->_cache->get($this->calculateKey($id)); + return $data===false?'':$data; + } + + /** + * Session write handler. + * Do not call this method directly. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function writeSession($id,$data) + { + return $this->_cache->set($this->calculateKey($id),$data,$this->getTimeout()); + } + + /** + * Session destroy handler. + * Do not call this method directly. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + return $this->_cache->delete($this->calculateKey($id)); + } + + /** + * Generates a unique key used for storing session data in cache. + * @param string session variable name + * @return string a safe cache key associated with the session variable name + */ + protected function calculateKey($id) + { + return self::CACHE_KEY_PREFIX.$id; + } +} diff --git a/web/framework-1.0.8/web/CClientScript.php b/web/framework-1.0.8/web/CClientScript.php new file mode 100644 index 0000000..fee4b5d --- /dev/null +++ b/web/framework-1.0.8/web/CClientScript.php @@ -0,0 +1,562 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CClientScript manages JavaScript and CSS stylesheets for views. + * + * @author Qiang Xue + * @version $Id: CClientScript.php 1090 2009-06-04 19:59:55Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CClientScript extends CApplicationComponent +{ + /** + * The script is rendered in the head section right before the title element. + */ + const POS_HEAD=0; + /** + * The script is rendered at the beginning of the body section. + */ + const POS_BEGIN=1; + /** + * The script is rendered at the end of the body section. + */ + const POS_END=2; + /** + * The script is rendered inside window onload function. + */ + const POS_LOAD=3; + /** + * The body script is rendered inside a jQuery ready function. + */ + const POS_READY=4; + + /** + * @var boolean whether JavaScript should be enabled. Defaults to true. + */ + public $enableJavaScript=true; + /** + * @var array the mapping between script file names and the corresponding script URLs. + * The array keys are script file names (without directory part) and the array values are the corresponding URLs. + * If an array value is false, the corresponding script file will not be rendered. + * If an array key is '*.js' or '*.css', the corresponding URL will replace all + * all JavaScript files or CSS files, respectively. + * + * This property is mainly used to optimize the generated HTML pages + * by merging different scripts files into fewer and optimized script files. + * @since 1.0.3 + */ + public $scriptMap=array(); + /** + * @var array the registered CSS files (CSS URL=>media type). + * @since 1.0.4 + */ + protected $cssFiles=array(); + /** + * @var array the registered JavaScript files (position, key => URL) + * @since 1.0.4 + */ + protected $scriptFiles=array(); + /** + * @var array the registered JavaScript code blocks (position, key => code) + * @since 1.0.5 + */ + protected $scripts=array(); + + private $_hasScripts=false; + private $_packages; + private $_dependencies; + private $_baseUrl; + private $_coreScripts=array(); + private $_css=array(); + private $_metas=array(); + private $_links=array(); + + /** + * Cleans all registered scripts. + */ + public function reset() + { + $this->_hasScripts=false; + $this->_coreScripts=array(); + $this->cssFiles=array(); + $this->_css=array(); + $this->scriptFiles=array(); + $this->scripts=array(); + $this->_metas=array(); + $this->_links=array(); + + $this->recordCachingAction('clientScript','reset',array()); + } + + /** + * Renders the registered scripts. + * This method is called in {@link CController::render} when it finishes + * rendering content. CClientScript thus gets a chance to insert script tags + * at head and body sections in the HTML output. + * @param string the existing output that needs to be inserted with script tags + */ + public function render(&$output) + { + if(!$this->_hasScripts) + return; + + $this->renderCoreScripts(); + + if(!empty($this->scriptMap)) + $this->remapScripts(); + + $this->renderHead($output); + if($this->enableJavaScript) + { + $this->renderBodyBegin($output); + $this->renderBodyEnd($output); + } + } + + /** + * Uses {@link scriptMap} to re-map the registered scripts. + * @since 1.0.3 + */ + protected function remapScripts() + { + $cssFiles=array(); + foreach($this->cssFiles as $url=>$media) + { + $name=basename($url); + if(isset($this->scriptMap[$name])) + { + if($this->scriptMap[$name]!==false) + $cssFiles[$this->scriptMap[$name]]=$media; + } + else if(isset($this->scriptMap['*.css'])) + { + if($this->scriptMap['*.css']!==false) + $cssFiles[$this->scriptMap['*.css']]=$media; + } + else + $cssFiles[$url]=$media; + } + $this->cssFiles=$cssFiles; + + $jsFiles=array(); + foreach($this->scriptFiles as $position=>$scripts) + { + $jsFiles[$position]=array(); + foreach($scripts as $key=>$script) + { + $name=basename($script); + if(isset($this->scriptMap[$name])) + { + if($this->scriptMap[$name]!==false) + $jsFiles[$position][$this->scriptMap[$name]]=$this->scriptMap[$name]; + } + else if(isset($this->scriptMap['*.js'])) + { + if($this->scriptMap['*.js']!==false) + $jsFiles[$position][$this->scriptMap['*.js']]=$this->scriptMap['*.js']; + } + else + $jsFiles[$position][$key]=$script; + } + } + $this->scriptFiles=$jsFiles; + } + + /** + * Renders the specified core javascript library. + * @since 1.0.3 + */ + public function renderCoreScripts() + { + if($this->_packages===null) + return; + $baseUrl=$this->getCoreScriptUrl(); + $cssFiles=array(); + $jsFiles=array(); + foreach($this->_coreScripts as $name) + { + foreach($this->_packages[$name] as $path) + { + $url=$baseUrl.'/'.$path; + if(substr($path,-4)==='.css') + $cssFiles[$url]=''; + else + $jsFiles[$url]=$url; + } + } + // merge in place + if($cssFiles!==array()) + { + foreach($this->cssFiles as $cssFile=>$media) + $cssFiles[$cssFile]=$media; + $this->cssFiles=$cssFiles; + } + if($jsFiles!==array()) + { + if(isset($this->scriptFiles[self::POS_HEAD])) + { + foreach($this->scriptFiles[self::POS_HEAD] as $url) + $jsFiles[$url]=$url; + } + $this->scriptFiles[self::POS_HEAD]=$jsFiles; + } + } + + /** + * Inserts the scripts in the head section. + * @param string the output to be inserted with scripts. + */ + public function renderHead(&$output) + { + $html=''; + foreach($this->_metas as $meta) + $html.=CHtml::metaTag($meta['content'],null,null,$meta); + foreach($this->_links as $link) + $html.=CHtml::linkTag(null,null,null,null,$link); + foreach($this->cssFiles as $url=>$media) + $html.=CHtml::cssFile($url,$media)."\n"; + foreach($this->_css as $css) + $html.=CHtml::css($css[0],$css[1])."\n"; + if($this->enableJavaScript) + { + if(isset($this->scriptFiles[self::POS_HEAD])) + { + foreach($this->scriptFiles[self::POS_HEAD] as $scriptFile) + $html.=CHtml::scriptFile($scriptFile)."\n"; + } + + if(isset($this->scripts[self::POS_HEAD])) + $html.=CHtml::script(implode("\n",$this->scripts[self::POS_HEAD]))."\n"; + } + + if($html!=='') + { + $count=0; + $output=preg_replace('/(]*>|<\\/head\s*>)/is','<###head###>$1',$output,1,$count); + if($count) + $output=str_replace('<###head###>',$html,$output); + else + $output=$html.$output; + } + } + + /** + * Inserts the scripts at the beginning of the body section. + * @param string the output to be inserted with scripts. + */ + public function renderBodyBegin(&$output) + { + $html=''; + if(isset($this->scriptFiles[self::POS_BEGIN])) + { + foreach($this->scriptFiles[self::POS_BEGIN] as $scriptFile) + $html.=CHtml::scriptFile($scriptFile)."\n"; + } + if(isset($this->scripts[self::POS_BEGIN])) + $html.=CHtml::script(implode("\n",$this->scripts[self::POS_BEGIN]))."\n"; + + if($html!=='') + { + $count=0; + $output=preg_replace('/(]*>)/is','$1<###begin###>',$output,1,$count); + if($count) + $output=str_replace('<###begin###>',$html,$output); + else + $output=$html.$output; + } + } + + /** + * Inserts the scripts at the end of the body section. + * @param string the output to be inserted with scripts. + */ + public function renderBodyEnd(&$output) + { + if(!isset($this->scriptFiles[self::POS_END]) && !isset($this->scripts[self::POS_END]) + && !isset($this->scripts[self::POS_READY]) && !isset($this->scripts[self::POS_LOAD])) + return; + + $fullPage=0; + $output=preg_replace('/(<\\/body\s*>)/is','<###end###>$1',$output,1,$fullPage); + $html=''; + if(isset($this->scriptFiles[self::POS_END])) + { + foreach($this->scriptFiles[self::POS_END] as $scriptFile) + $html.=CHtml::scriptFile($scriptFile)."\n"; + } + $scripts=isset($this->scripts[self::POS_END]) ? $this->scripts[self::POS_END] : array(); + if(isset($this->scripts[self::POS_READY])) + { + if($fullPage) + $scripts[]="jQuery(document).ready(function() {\n".implode("\n",$this->scripts[self::POS_READY])."\n});"; + else + $scripts[]=implode("\n",$this->scripts[self::POS_READY]); + } + if(isset($this->scripts[self::POS_LOAD])) + { + if($fullPage) + $scripts[]="window.onload=function() {\n".implode("\n",$this->scripts[self::POS_LOAD])."\n};"; + else + $scripts[]=implode("\n",$this->scripts[self::POS_LOAD]); + } + if(!empty($scripts)) + $html.=CHtml::script(implode("\n",$scripts))."\n"; + + if($fullPage) + $output=str_replace('<###end###>',$html,$output); + else + $output=$output.$html; + } + + /** + * Returns the base URL of all core javascript files. + * If the base URL is not explicitly set, this method will publish the whole directory + * 'framework/web/js/source' and return the corresponding URL. + * @return string the base URL of all core javascript files + */ + public function getCoreScriptUrl() + { + if($this->_baseUrl!==null) + return $this->_baseUrl; + else + return $this->_baseUrl=Yii::app()->getAssetManager()->publish(YII_PATH.'/web/js/source'); + } + + /** + * Sets the base URL of all core javascript files. + * This setter is provided in case when core javascript files are manually published + * to a pre-specified location. This may save asset publishing time for large-scale applications. + * @param string the base URL of all core javascript files. + */ + public function setCoreScriptUrl($value) + { + $this->_baseUrl=$value; + } + + /** + * Registers a core javascript library. + * @param string the core javascript library name + * @see renderCoreScript + */ + public function registerCoreScript($name) + { + return; + if(isset($this->_coreScripts[$name])) + return; + + if($this->_packages===null) + { + $config=require(YII_PATH.'/web/js/packages.php'); + $this->_packages=$config[0]; + $this->_dependencies=$config[1]; + } + if(!isset($this->_packages[$name])) + return; + if(isset($this->_dependencies[$name])) + { + foreach($this->_dependencies[$name] as $depName) + $this->registerCoreScript($depName); + } + + $this->_hasScripts=true; + $this->_coreScripts[$name]=$name; + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerCoreScript',$params); + } + + /** + * Registers a CSS file + * @param string URL of the CSS file + * @param string media that the CSS file should be applied to. If empty, it means all media types. + */ + public function registerCssFile($url,$media='') + { + return; + $this->_hasScripts=true; + $this->cssFiles[$url]=$media; + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerCssFile',$params); + } + + /** + * Registers a piece of CSS code. + * @param string ID that uniquely identifies this piece of CSS code + * @param string the CSS code + * @param string media that the CSS code should be applied to. If empty, it means all media types. + */ + public function registerCss($id,$css,$media='') + { + $this->_hasScripts=true; + $this->_css[$id]=array($css,$media); + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerCss',$params); + } + + /** + * Registers a javascript file. + * @param string URL of the javascript file + * @param integer the position of the JavaScript code. Valid values include the following: + *
      + *
    • CClientScript::POS_HEAD : the script is inserted in the head section right before the title element.
    • + *
    • CClientScript::POS_BEGIN : the script is inserted at the beginning of the body section.
    • + *
    • CClientScript::POS_END : the script is inserted at the end of the body section.
    • + *
    + */ + public function registerScriptFile($url,$position=self::POS_HEAD) + { + return; + $this->_hasScripts=true; + $this->scriptFiles[$position][$url]=$url; + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerScriptFile',$params); + } + + /** + * Registers a piece of javascript code. + * @param string ID that uniquely identifies this piece of JavaScript code + * @param string the javascript code + * @param integer the position of the JavaScript code. Valid values include the following: + *
      + *
    • CClientScript::POS_HEAD : the script is inserted in the head section right before the title element.
    • + *
    • CClientScript::POS_BEGIN : the script is inserted at the beginning of the body section.
    • + *
    • CClientScript::POS_END : the script is inserted at the end of the body section.
    • + *
    • CClientScript::POS_LOAD : the script is inserted in the window.onload() function.
    • + *
    • CClientScript::POS_READY : the script is inserted in the jQuery's ready function.
    • + *
    + */ + public function registerScript($id,$script,$position=self::POS_READY) + { + $this->_hasScripts=true; + $this->scripts[$position][$id]=$script; + if($position===self::POS_READY) + $this->registerCoreScript('jquery'); + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerScript',$params); + } + + /** + * Registers a meta tag that will be inserted in the head section (right before the title element) of the resulting page. + * @param string content attribute of the meta tag + * @param string name attribute of the meta tag. If null, the attribute will not be generated + * @param string http-equiv attribute of the meta tag. If null, the attribute will not be generated + * @param array other options in name-value pairs (e.g. 'scheme', 'lang') + * @since 1.0.1 + */ + public function registerMetaTag($content,$name=null,$httpEquiv=null,$options=array()) + { + $this->_hasScripts=true; + $options['content']=$content; + if($name!==null) + $options['name']=$name; + if($httpEquiv!==null) + $options['http-equiv']=$httpEquiv; + $this->_metas[serialize($options)]=$options; + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerMetaTag',$params); + } + + /** + * Registers a link tag that will be inserted in the head section (right before the title element) of the resulting page. + * @param string rel attribute of the link tag. If null, the attribute will not be generated. + * @param string type attribute of the link tag. If null, the attribute will not be generated. + * @param string href attribute of the link tag. If null, the attribute will not be generated. + * @param string media attribute of the link tag. If null, the attribute will not be generated. + * @param array other options in name-value pairs + * @since 1.0.1 + */ + public function registerLinkTag($relation=null,$type=null,$href=null,$media=null,$options=array()) + { + $this->_hasScripts=true; + if($relation!==null) + $options['rel']=$relation; + if($type!==null) + $options['type']=$type; + if($href!==null) + $options['href']=$href; + if($media!==null) + $options['media']=$media; + $this->_links[serialize($options)]=$options; + $params=func_get_args(); + $this->recordCachingAction('clientScript','registerLinkTag',$params); + } + + /** + * Checks whether the CSS file has been registered. + * @param string URL of the CSS file + * @return boolean whether the CSS file is already registered + */ + public function isCssFileRegistered($url) + { + return isset($this->cssFiles[$url]); + } + + /** + * Checks whether the CSS code has been registered. + * @param string ID that uniquely identifies the CSS code + * @return boolean whether the CSS code is already registered + */ + public function isCssRegistered($id) + { + return isset($this->_css[$id]); + } + + /** + * Checks whether the JavaScript file has been registered. + * @param string URL of the javascript file + * @param integer the position of the JavaScript code. Valid values include the following: + *
      + *
    • CClientScript::POS_HEAD : the script is inserted in the head section right before the title element.
    • + *
    • CClientScript::POS_BEGIN : the script is inserted at the beginning of the body section.
    • + *
    • CClientScript::POS_END : the script is inserted at the end of the body section.
    • + *
    + * @return boolean whether the javascript file is already registered + */ + public function isScriptFileRegistered($url,$position=self::POS_HEAD) + { + return isset($this->scriptFiles[$position][$url]); + } + + /** + * Checks whether the JavaScript code has been registered. + * @param string ID that uniquely identifies the JavaScript code + * @param integer the position of the JavaScript code. Valid values include the following: + *
      + *
    • CClientScript::POS_HEAD : the script is inserted in the head section right before the title element.
    • + *
    • CClientScript::POS_BEGIN : the script is inserted at the beginning of the body section.
    • + *
    • CClientScript::POS_END : the script is inserted at the end of the body section.
    • + *
    • CClientScript::POS_LOAD : the script is inserted in the window.onload() function.
    • + *
    • CClientScript::POS_READY : the script is inserted in the jQuery's ready function.
    • + *
    + * @return boolean whether the javascript code is already registered + */ + public function isScriptRegistered($id,$position=self::POS_READY) + { + return isset($this->scripts[$position][$id]); + } + + /** + * Records a method call when an output cache is in effect. + * This is a shortcut to Yii::app()->controller->recordCachingAction. + * In case when controller is absent, nothing is recorded. + * @param string a property name of the controller. It refers to an object + * whose method is being called. If empty it means the controller itself. + * @param string the method name + * @param array parameters passed to the method + * @see COutputCache + * @since 1.0.5 + */ + protected function recordCachingAction($context,$method,$params) + { + if(($controller=Yii::app()->getController())!==null) + $controller->recordCachingAction($context,$method,$params); + } +} diff --git a/web/framework-1.0.8/web/CController.php b/web/framework-1.0.8/web/CController.php new file mode 100644 index 0000000..2285111 --- /dev/null +++ b/web/framework-1.0.8/web/CController.php @@ -0,0 +1,1072 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CController manages a set of actions which deal with the corresponding user requests. + * + * Through the actions, CController coordinates the data flow between models and views. + * + * When a user requests an action 'XYZ', CController will do one of the following: + * 1. Method-based action: call method 'actionXYZ' if it exists; + * 2. Class-based action: create an instance of class 'XYZ' if the class is found in the action class map + * (specified via {@link actions()}, and execute the action; + * 3. Call {@link missingAction()}, which by default will raise a 404 HTTP exception. + * + * If the user does not specify an action, CController will run the action specified by + * {@link defaultAction}, instead. + * + * CController may be configured to execute filters before and after running actions. + * Filters preprocess/postprocess the user request/response and may quit executing actions + * if needed. They are executed in the order they are specified. If during the execution, + * any of the filters returns true, the rest filters and the action will no longer get executed. + * + * Filters can be individual objects, or methods defined in the controller class. + * They are specified by overriding {@link filters()} method. The following is an example + * of the filter specification: + *
    + * array(
    + *     'accessControl - login',
    + *     'ajaxOnly + search',
    + *     array(
    + *         'COutputCache + list',
    + *         'duration'=>300,
    + *     ),
    + * )
    + * 
    + * The above example declares three filters: accessControl, ajaxOnly, COutputCache. The first two + * are method-based filters (defined in CController), which refer to filtering methods in the controller class; + * while the last refers to a object-based filter whose class is 'system.web.widgets.COutputCache' and + * the 'duration' property is initialized as 300 (s). + * + * For method-based filters, a method named 'filterXYZ($filterChain)' in the controller class + * will be executed, where 'XYZ' stands for the filter name as specified in {@link filters()}. + * Note, inside the filter method, you must call $filterChain->run() if the action should + * be executed. Otherwise, the filtering process would stop at this filter. + * + * Filters can be specified so that they are executed only when running certain actions. + * For method-based filters, this is done by using '+' and '-' operators in the filter specification. + * The '+' operator means the filter runs only when the specified actions are requested; + * while the '-' operator means the filter runs only when the requested action is not among those actions. + * For object-based filters, the '+' and '-' operators are following the class name. + * + * @author Qiang Xue + * @version $Id: CController.php 1158 2009-06-22 16:51:20Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CController extends CBaseController +{ + /** + * Name of the hidden field storing persistent page states. + */ + const STATE_INPUT_NAME='YII_PAGE_STATE'; + + /** + * @var mixed the name of the layout to be applied to this controller's views. + * Defaults to null, meaning the {@link CWebApplication::layout application layout} + * is used. If it is false, no layout will be applied. + * Since version 1.0.3, the {@link CWebModule::layout module layout} will be used + * if the controller belongs to a module and this layout property is null. + */ + public $layout; + /** + * @var string the name of the default action. Defaults to 'index'. + */ + public $defaultAction='index'; + + private $_id; + private $_action; + private $_pageTitle; + private $_cachingStack; + private $_clips; + private $_dynamicOutput; + private $_pageStates; + private $_module; + + + /** + * @param string id of this controller + * @param CWebModule the module that this controller belongs to. This parameter + * has been available since version 1.0.3. + */ + public function __construct($id,$module=null) + { + $this->_id=$id; + $this->_module=$module; + $this->attachBehaviors($this->behaviors()); + } + + /** + * Initializes the controller. + * This method is called by the application before the controller starts to execute. + * You may override this method to perform the needed initialization for the controller. + * @since 1.0.1 + */ + public function init() + { + } + + /** + * Returns the filter configurations. + * + * By overriding this method, child classes can specify filters to be applied to actions. + * + * This method returns an array of filter specifications. Each array element specify a single filter. + * + * For a method-based filter (called inline filter), it is specified as 'FilterName[ +|- Action1, Action2, ...]', + * where the '+' ('-') operators describe which actions should be (should not be) applied with the filter. + * + * For a class-based filter, it is specified as an array like the following: + *
    +	 * array(
    +	 *     'FilterClass[ +|- Action1, Action2, ...]',
    +	 *     'name1'=>'value1',
    +	 *     'name2'=>'value2',
    +	 *     ...
    +	 * )
    +	 * 
    + * where the name-value pairs will be used to initialize the properties of the filter. + * + * Note, in order to inherit filters defined in the parent class, a child class needs to + * merge the parent filters with child filters using functions like array_merge(). + * + * @return array a list of filter configurations. + * @see CFilter + */ + public function filters() + { + return array(); + } + + /** + * Returns a list of external action classes. + * Array keys are action IDs, and array values are the corresponding + * action class in dot syntax (e.g. 'edit'=>'application.controllers.article.EditArticle') + * or arrays representing the configuration of the actions, such as the following, + *
    +	 * return array(
    +	 *     'action1'=>'path.to.Action1Class',
    +	 *     'action2'=>array(
    +	 *         'class'=>'path.to.Action2Class',
    +	 *         'property1'=>'value1',
    +	 *         'property2'=>'value2',
    +	 *     ),
    +	 * );
    +	 * 
    + * Derived classes may override this method to declare external actions. + * + * Note, in order to inherit actions defined in the parent class, a child class needs to + * merge the parent actions with child actions using functions like array_merge(). + * + * Since version 1.0.1, you may import actions from an action provider + * (such as a widget, see {@link CWidget::actions}), like the following: + *
    +	 * return array(
    +	 *     ...other actions...
    +	 *     // import actions declared in ProviderClass::actions()
    +	 *     // the action IDs will be prefixed with 'pro.'
    +	 *     'pro.'=>'path.to.ProviderClass',
    +	 *     // similar as above except that the imported actions are
    +	 *     // configured with the specified initial property values
    +	 *     'pro2.'=>array(
    +	 *         'class'=>'path.to.ProviderClass',
    +	 *         'action1'=>array(
    +	 *             'property1'=>'value1',
    +	 *         ),
    +	 *         'action2'=>array(
    +	 *             'property2'=>'value2',
    +	 *         ),
    +	 *     ),
    +	 * )
    +	 * 
    + * + * In the above, we differentiate action providers from other action + * declarations by the array keys. For action providers, the array keys + * must contain a dot. As a result, an action ID 'pro2.action1' will + * be resolved as the 'action1' action declared in the 'ProviderClass'. + * + * @return array list of external action classes + * @see createAction + */ + public function actions() + { + return array(); + } + + /** + * Returns a list of behaviors that this controller should behave as. + * The return value should be an array of behavior configurations indexed by + * behavior names. Each behavior configuration can be either a string specifying + * the behavior class or an array of the following structure: + *
    +	 * 'behaviorName'=>array(
    +	 *     'class'=>'path.to.BehaviorClass',
    +	 *     'property1'=>'value1',
    +	 *     'property2'=>'value2',
    +	 * )
    +	 * 
    + * + * Note, the behavior classes must implement {@link IBehavior} or extend from + * {@link CBehavior}. Behaviors declared in this method will be attached + * to the model when it is instantiated. + * + * For more details about behaviors, see {@link CComponent}. + * @return array the behavior configurations (behavior name=>behavior configuration) + * @since 1.0.6 + */ + public function behaviors() + { + return array(); + } + + /** + * Returns the access rules for this controller. + * Override this method if you use the {@link filterAccessControl accessControl} filter. + * @return array list of access rules. See {@link CAccessControlFilter} for details about rule specification. + */ + public function accessRules() + { + return array(); + } + + /** + * Runs the named action. + * Filters specified via {@link filters()} will be applied. + * @param string action ID + * @throws CHttpException if the action does not exist or the action name is not proper. + * @see filters + * @see createAction + * @see runAction + */ + public function run($actionID) + { + if(($action=$this->createAction($actionID))!==null) + { + if(($parent=$this->getModule())===null) + $parent=Yii::app(); + if($parent->beforeControllerAction($this,$action)) + { + $this->runActionWithFilters($action,$this->filters()); + $parent->afterControllerAction($this,$action); + } + } + else + $this->missingAction($actionID); + } + + /** + * Runs an action with the specified filters. + * A filter chain will be created based on the specified filters + * and the action will be executed then. + * @param CAction the action to be executed. + * @param array list of filters to be applied to the action. + * @see filters + * @see createAction + * @see runAction + */ + public function runActionWithFilters($action,$filters) + { + if(empty($filters)) + $this->runAction($action); + else + { + $priorAction=$this->_action; + $this->_action=$action; + CFilterChain::create($this,$action,$filters)->run(); + $this->_action=$priorAction; + } + } + + /** + * Runs the action after passing through all filters. + * This method is invoked by {@link runActionWithFilters} after all possible filters have been executed + * and the action starts to run. + * @param CAction action to run + */ + public function runAction($action) + { + $priorAction=$this->_action; + $this->_action=$action; + if($this->beforeAction($action)) + { + $action->run(); + $this->afterAction($action); + } + $this->_action=$priorAction; + } + + /** + * Postprocesses the output generated by {@link render()}. + * This method is invoked at the end of {@link render()} and {@link renderText()}. + * If there are registered client scripts, this method will insert them into the output + * at appropriate places. If there are dynamic contents, they will also be inserted. + * This method may also save the persistent page states in hidden fields of + * stateful forms in the page. + * @param string the output generated by the current action + * @return string the output that has been processed. + */ + public function processOutput($output) + { + Yii::app()->getClientScript()->render($output); + + // if using page caching, we should delay dynamic output replacement + if($this->_dynamicOutput!==null && $this->isCachingStackEmpty()) + $output=$this->processDynamicOutput($output); + + if($this->_pageStates===null) + $this->_pageStates=$this->loadPageStates(); + if(!empty($this->_pageStates)) + $this->savePageStates($this->_pageStates,$output); + + return $output; + } + + /** + * Postprocesses the dynamic output. + * This method is internally used. Do not call this method directly. + * @param string output to be processed + * @return string the processed output + * @since 1.0.4 + */ + public function processDynamicOutput($output) + { + if($this->_dynamicOutput) + { + $output=preg_replace_callback('/<###dynamic-(\d+)###>/',array($this,'replaceDynamicOutput'),$output); + $this->_dynamicOutput=null; + } + return $output; + } + + /** + * Replaces the dynamic content placeholders with actual content. + * This is a callback function used internally. + * @param array matches + * @return string the replacement + * @see processOutput + */ + protected function replaceDynamicOutput($matches) + { + return isset($this->_dynamicOutput[$matches[1]]) ? $this->_dynamicOutput[$matches[1]] : $matches[0]; + } + + /** + * Creates the action instance based on the action name. + * The action can be either an inline action or an object. + * The latter is created by looking up the action map specified in {@link actions}. + * @param string ID of the action. If empty, the {@link defaultAction default action} will be used. + * @return CAction the action instance, null if the action does not exist. + * @see actions + */ + public function createAction($actionID) + { + if($actionID==='') + $actionID=$this->defaultAction; + if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method + return new CInlineAction($this,$actionID); + else + return $this->createActionFromMap($this->actions(),$actionID,$actionID); + } + + /** + * Creates the action instance based on the action map. + * This method will check to see if the action ID appears in the given + * action map. If so, the corresponding configuration will be used to + * create the action instance. + * @param array the action map + * @param string the action ID that has its prefix stripped off + * @param string the originally requested action ID + * @param array the action configuration that should be applied on top of the configuration specified in the map + * @return CAction the action instance, null if the action does not exist. + * @since 1.0.1 + */ + protected function createActionFromMap($actionMap,$actionID,$requestActionID,$config=array()) + { + if(($pos=strpos($actionID,'.'))===false && isset($actionMap[$actionID])) + { + $baseConfig=is_array($actionMap[$actionID]) ? $actionMap[$actionID] : array('class'=>$actionMap[$actionID]); + return Yii::createComponent(empty($config)?$baseConfig:array_merge($baseConfig,$config),$this,$requestActionID); + } + else if($pos===false) + return null; + + // the action is defined in a provider + $prefix=substr($actionID,0,$pos+1); + if(!isset($actionMap[$prefix])) + return null; + $actionID=(string)substr($actionID,$pos+1); + + $provider=$actionMap[$prefix]; + if(is_string($provider)) + $providerType=$provider; + else if(is_array($provider) && isset($provider['class'])) + { + $providerType=$provider['class']; + if(isset($provider[$actionID])) + { + if(is_string($provider[$actionID])) + $config=array_merge(array('class'=>$provider[$actionID]),$config); + else + $config=array_merge($provider[$actionID],$config); + } + } + else + throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.')); + + $class=Yii::import($providerType,true); + $map=call_user_func(array($class,'actions')); + + return $this->createActionFromMap($map,$actionID,$requestActionID,$config); + } + + /** + * Handles the request whose action is not recognized. + * This method is invoked when the controller cannot find the requested action. + * The default implementation simply throws an exception. + * @param string the missing action name + * @throws CHttpException whenever this method is invoked + */ + public function missingAction($actionID) + { + throw new CHttpException(404,Yii::t('yii','The system is unable to find the requested action "{action}".', + array('{action}'=>$actionID==''?$this->defaultAction:$actionID))); + } + + /** + * @return CAction the action currently being executed, null if no active action. + */ + public function getAction() + { + return $this->_action; + } + + /** + * @param CAction the action currently being executed. + */ + public function setAction($value) + { + $this->_action=$value; + } + + /** + * @return string ID of the controller + */ + public function getId() + { + return $this->_id; + } + + /** + * @return string the controller ID that is prefixed with the module ID (if any). + * @since 1.0.3 + */ + public function getUniqueId() + { + return $this->_module ? $this->_module->getId().'/'.$this->_id : $this->_id; + } + + /** + * @return CWebModule the module that this controller belongs to. It returns null + * if the controller does not belong to any module + * @since 1.0.3 + */ + public function getModule() + { + return $this->_module; + } + + /** + * Returns the directory containing view files for this controller. + * The default implementation returns 'protected/views/ControllerID'. + * Child classes may override this method to use customized view path. + * If the controller belongs to a module (since version 1.0.3), the default view path + * is the {@link CWebModule::getViewPath module view path} appended with the controller ID. + * @return string the directory containing the view files for this controller. Defaults to 'protected/views/ControllerID'. + */ + public function getViewPath() + { + // if(($module=$this->getModule())===null) + // $module=Yii::app(); + // return $module->getViewPath().'/'.$this->getId(); + + $app = Yii::app(); + $path = preg_replace('|/([^/]*)$|', '', $app->classFile); + return $path; + } + + /** + * Looks for the view file according to the given view name. + * This method will look for the view under the controller's {@link getViewPath viewPath}. + * If the view name starts with '/', the view will be looked for under the application's + * {@link CWebApplication::getViewPath viewPath}. + * The view script file is named as "ViewName.php". A localized view file + * may be returned if internationalization is needed. See {@link CApplication::findLocalizedFile} + * for more details. + * Since version 1.0.2, the view name can also refer to a path alias + * if it contains dot characters. + * Since version 1.0.3, if the controller belongs to a module, the view file + * will be searched under the {@link CWebModule::getViewPath module view path}. + * @param string name of the view (without file extension) + * @return string the view file path, false if the view file does not exist + * @see CApplication::findLocalizedFile + */ + public function getViewFile($viewName) + { + if(($theme=Yii::app()->getTheme())!==null && ($viewFile=$theme->getViewFile($this,$viewName))!==false) + return $viewFile; + $module=$this->getModule(); + $basePath=$module ? $module->getViewPath() : Yii::app()->getViewPath(); + return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath); + } + + /** + * Looks for the view script file for a layout. + * This method will look for the view under the application's {@link CWebApplication::getLayoutPath layoutPath}. + * If the view name starts with '/', the view will be looked for under the application's + * {@link CWebApplication::getViewPath viewPath}. + * If the view name is null, the application's {@link CWebApplication::layout default layout} + * will be used. If the view name is false, this method simply returns false. + * Since version 1.0.2, the view name can also refer to a path alias + * if it contains dot characters. + * Since version 1.0.3, if the controller belongs to a module, the view file + * will be searched under the {@link CWebModule::getViewPath module layout path}, + * and if the view name is null, the {@link CWebModule::layout module default layout} + * will be used. + * @param mixed layout name + * @return string the view file for the layout. False if the view file cannot be found + */ + public function getLayoutFile($layoutName) + { + if($layoutName===false) + return false; + if(($theme=Yii::app()->getTheme())!==null && ($layoutFile=$theme->getLayoutFile($this,$layoutName))!==false) + return $layoutFile; + + if(empty($layoutName)) + { + $module=$this->getModule(); + while($module!==null) + { + if($module->layout===false) + return false; + if(!empty($module->layout)) + break; + $module=$module->getParentModule(); + } + if($module===null) + $module=Yii::app(); + return $this->resolveViewFile($module->layout,$module->getLayoutPath(),$module->getViewPath()); + } + else + { + if(($module=$this->getModule())===null) + $module=Yii::app(); + return $this->resolveViewFile($layoutName,$module->getLayoutPath(),$module->getViewPath()); + } + } + + /** + * Finds a view file based on its name. + * The view name can be in one of the following formats: + *
      + *
    • absolute view: the view name starts with a slash '/'.
    • + *
    • aliased view: the view name contains dots and refers to a path alias. + * The view file is determined by calling {@link YiiBase::getPathOfAlias()}.
    • + *
    • relative view: otherwise.
    • + *
    + * For absolute view and relative view, the corresponding view file is a PHP file + * whose name is the same as the view name. The file is located under a specified directory. + * This method will call {@link CApplication::findLocalizedFile} to search for a localized file, if any. + * @param string the view name + * @param string the directory that is used to search for a relative view name + * @param string the directory that is used to search for an absolute view name + * @return mixed the view file path. False if the view file does not exist. + * @since 1.0.3 + */ + public function resolveViewFile($viewName,$viewPath,$basePath) + { + if(empty($viewName)) + return false; + if($viewName[0]==='/') + $viewFile=$basePath.$viewName.'.php'; + else if(strpos($viewName,'.')) + $viewFile=Yii::getPathOfAlias($viewName).'.php'; + else + { + // error_log("viewpath $viewPath, viewname $viewName"); + $viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName.'.php'; + } + + // error_log("viewfile {$viewFile}"); + return is_file($viewFile) ? Yii::app()->findLocalizedFile($viewFile) : false; + } + + /** + * Returns the list of clips. + * A clip is a named piece of rendering result that can be + * inserted at different places. + * @return CMap the list of clips + * @see CClipWidget + */ + public function getClips() + { + if($this->_clips!==null) + return $this->_clips; + else + return $this->_clips=new CMap; + } + + /** + * Renders a view with a layout. + * + * This method first calls {@link renderPartial} to render the view (called content view). + * It then renders the layout view which may embed the content view at appropriate place. + * In the layout view, the content view rendering result can be accessed via variable + * $content. At the end, it calls {@link processOutput} to insert scripts + * and dynamic contents if they are available. + * + * By default, the layout view script is "protected/views/layouts/main.php". + * This may be customized by changing {@link layout}. + * + * @param string name of the view to be rendered. See {@link getViewFile} for details + * about how the view script is resolved. + * @param array data to be extracted into PHP variables and made available to the view script + * @param boolean whether the rendering result should be returned instead of being displayed to end users. + * @return string the rendering result. Null if the rendering result is not required. + * @see renderPartial + * @see getLayoutFile + */ + public function render($view,$data=null,$return=false) + { + $output=$this->renderPartial($view,$data,true); + + if(($layoutFile=$this->getLayoutFile($this->layout))!==false) + $output=$this->renderFile($layoutFile,array('content'=>$output),true); + + $output=$this->processOutput($output); + + if($return) + return $output; + else + echo $output; + } + + /** + * Renders a static text string. + * The string will be inserted in the current controller layout and returned back. + * @param string the static text string + * @param boolean whether the rendering result should be returned instead of being displayed to end users. + * @return string the rendering result. Null if the rendering result is not required. + * @see getLayoutFile + */ + public function renderText($text,$return=false) + { + if(($layoutFile=$this->getLayoutFile($this->layout))!==false) + $text=$this->renderFile($layoutFile,array('content'=>$text),true); + + $text=$this->processOutput($text); + + if($return) + return $text; + else + echo $text; + } + + /** + * Renders a view. + * + * The named view refers to a PHP script (resolved via {@link getViewFile}) + * that is included by this method. If $data is an associative array, + * it will be extracted as PHP variables and made available to the script. + * + * This method differs from {@link render()} in that it does not + * apply a layout to the rendered result. It is thus mostly used + * in rendering a partial view, or an AJAX response. + * + * @param string name of the view to be rendered. See {@link getViewFile} for details + * about how the view script is resolved. + * @param array data to be extracted into PHP variables and made available to the view script + * @param boolean whether the rendering result should be returned instead of being displayed to end users + * @param boolean whether the rendering result should be postprocessed using {@link processOutput}. + * This parameter should be set true if renderPartial is the only method used to generate + * the output when handling a user request. + * @return string the rendering result. Null if the rendering result is not required. + * @throws CException if the view does not exist + * @see getViewFile + * @see processOutput + * @see render + */ + public function renderPartial($view,$data=null,$return=false,$processOutput=false) + { + if(($viewFile=$this->getViewFile($view))!==false) + { + $output=$this->renderFile($viewFile,$data,true); + if($processOutput) + $output=$this->processOutput($output); + if($return) + return $output; + else + echo $output; + } + else + throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".', + array('{controller}'=>get_class($this), '{view}'=>$view))); + } + + /** + * Renders dynamic content returned by the specified callback. + * This method is used together with {@link COutputCache}. Dynamic contents + * will always show as their latest state even if the content surrounding them is being cached. + * This is especially useful when caching pages that are mostly static but contain some small + * dynamic regions, such as username or current time. + * We can use this method to render these dynamic regions to ensure they are always up-to-date. + * + * The first parameter to this method should be a valid PHP callback, while the rest parameters + * will be passed to the callback. + * + * Note, the callback and its parameter values will be serialized and saved in cache. + * Make sure they are serializable. + * + * @param callback a PHP callback which returns the needed dynamic content. + * When the callback is specified as a string, it will be first assumed to be a method of the current + * controller class. If the method does not exist, it is assumed to be a global PHP function. + * Note, the callback should return the dynamic content instead of echoing it. + */ + public function renderDynamic($callback) + { + $n=count($this->_dynamicOutput); + echo "<###dynamic-$n###>"; + $params=func_get_args(); + array_shift($params); + $this->renderDynamicInternal($callback,$params); + } + + /** + * This method is internally used. + * @param callback a PHP callback which returns the needed dynamic content. + * @param array parameters passed to the PHP callback + * @see renderDynamic + */ + public function renderDynamicInternal($callback,$params) + { + $this->recordCachingAction('','renderDynamicInternal',array($callback,$params)); + if(is_string($callback) && method_exists($this,$callback)) + $callback=array($this,$callback); + $this->_dynamicOutput[]=call_user_func_array($callback,$params); + } + + /** + * Creates a relative URL for the specified action defined in this controller. + * @param string the URL route. This should be in the format of 'ControllerID/ActionID'. + * If the ControllerID is not present, the current controller ID will be prefixed to the route. + * If the route is empty, it is assumed to be the current action. + * Since version 1.0.3, if the controller belongs to a module, the {@link CWebModule::getId module ID} + * will be prefixed to the route. (If you do not want the module ID prefix, the route should start with a slash '/'.) + * @param array additional GET parameters (name=>value). Both the name and value will be URL-encoded. + * If the name is '#', the corresponding value will be treated as an anchor + * and will be appended at the end of the URL. This anchor feature has been available since version 1.0.1. + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + public function createUrl($route,$params=array(),$ampersand='&') + { + if($route==='') + $route=$this->getId().'/'.$this->getAction()->getId(); + else if(strpos($route,'/')===false) + $route=$this->getId().'/'.$route; + if($route[0]!=='/' && ($module=$this->getModule())!==null) + $route=$module->getId().'/'.$route; + return Yii::app()->createUrl(trim($route,'/'),$params,$ampersand); + } + + /** + * Creates an absolute URL for the specified action defined in this controller. + * @param string the URL route. This should be in the format of 'ControllerID/ActionID'. + * If the ControllerPath is not present, the current controller ID will be prefixed to the route. + * If the route is empty, it is assumed to be the current action. + * @param array additional GET parameters (name=>value). Both the name and value will be URL-encoded. + * @param string schema to use (e.g. http, https). If empty, the schema used for the current request will be used. + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&') + { + return Yii::app()->getRequest()->getHostInfo($schema).$this->createUrl($route,$params,$ampersand); + } + + /** + * @return string the page title. Defaults to the controller name and the action name. + */ + public function getPageTitle() + { + if($this->_pageTitle!==null) + return $this->_pageTitle; + else + { + $name=ucfirst(basename($this->getId())); + if($this->getAction()!==null && strcasecmp($this->getAction()->getId(),$this->defaultAction)) + return $this->_pageTitle=Yii::app()->name.' - '.ucfirst($this->getAction()->getId()).' '.$name; + else + return $this->_pageTitle=Yii::app()->name.' - '.$name; + } + } + + /** + * @param string the page title. + */ + public function setPageTitle($value) + { + $this->_pageTitle=$value; + } + + /** + * Redirects the browser to the specified URL or route (controller/action). + * @param mixed the URL to be redirected to. If the parameter is an array, + * the first element must be a route to a controller action and the rest + * are GET parameters in name-value pairs. + * @param boolean whether to terminate the current application after calling this method + * @param integer the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} + * for details about HTTP status code. This parameter has been available since version 1.0.4. + */ + public function redirect($url,$terminate=true,$statusCode=302) + { + if(is_array($url)) + { + $route=isset($url[0]) ? $url[0] : ''; + $url=$this->createUrl($route,array_splice($url,1)); + } + Yii::app()->getRequest()->redirect($url,$terminate,$statusCode); + } + + /** + * Refreshes the current page. + * The effect of this method call is the same as user pressing the + * refresh button on the browser (without post data). + * @param boolean whether to terminate the current application after calling this method + * @param string the anchor that should be appended to the redirection URL. + * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it. + * The parameter has been available since version 1.0.7. + **/ + public function refresh($terminate=true,$anchor='') + { + $this->redirect(Yii::app()->getRequest()->getUrl().$anchor,$terminate); + } + + /** + * Records a method call when an output cache is in effect. + * When the content is served from the output cache, the recorded + * method will be re-invoked. + * @param string a property name of the controller. It refers to an object + * whose method is being called. If empty it means the controller itself. + * @param string the method name + * @param array parameters passed to the method + * @see COutputCache + */ + public function recordCachingAction($context,$method,$params) + { + if($this->_cachingStack) // record only when there is an active output cache + { + foreach($this->_cachingStack as $cache) + $cache->recordAction($context,$method,$params); + } + } + + /** + * @param boolean whether to create a stack if it does not exist yet. Defaults to true. + * @return CStack stack of {@link COutputCache} objects + */ + public function getCachingStack($createIfNull=true) + { + if(!$this->_cachingStack) + $this->_cachingStack=new CStack; + return $this->_cachingStack; + } + + /** + * @return whether the caching stack is empty. If not empty, it means currently there are + * some output cache in effect. Note, the return result of this method may change when it is + * called in different output regions, depending on the partition of output caches. + * @since 1.0.5 + */ + public function isCachingStackEmpty() + { + return $this->_cachingStack===null || !$this->_cachingStack->getCount(); + } + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param CAction the action to be executed. + * @return boolean whether the action should be executed. + */ + protected function beforeAction($action) + { + return true; + } + + /** + * This method is invoked right after an action is executed. + * You may override this method to do some postprocessing for the action. + * @param CAction the action just executed. + */ + protected function afterAction($action) + { + } + + /** + * The filter method for 'postOnly' filter. + * This filter reports an error if the applied action is receiving a non-POST request. + * @param CFilterChain the filter chain that the filter is on. + * @throws CHttpException if the current request is not a POST request + */ + public function filterPostOnly($filterChain) + { + if(Yii::app()->getRequest()->getIsPostRequest()) + $filterChain->run(); + else + throw new CHttpException(400,Yii::t('yii','Your request is not valid.')); + } + + /** + * The filter method for 'ajaxOnly' filter. + * This filter reports an error if the applied action is receiving a non-AJAX request. + * @param CFilterChain the filter chain that the filter is on. + * @throws CHttpException if the current request is not an AJAX request. + */ + public function filterAjaxOnly($filterChain) + { + if(Yii::app()->getRequest()->getIsAjaxRequest()) + $filterChain->run(); + else + throw new CHttpException(400,Yii::t('yii','Your request is not valid.')); + } + + /** + * The filter method for 'accessControl' filter. + * This filter is a wrapper of {@link CAccessControlFilter}. + * To use this filter, you must override {@link accessRules} method. + * @param CFilterChain the filter chain that the filter is on. + */ + public function filterAccessControl($filterChain) + { + $filter=new CAccessControlFilter; + $filter->setRules($this->accessRules()); + $filter->filter($filterChain); + } + + /** + * Generates pagination information. + * This method can be used to generate pagination information given item count + * and page size. The pagination information can then be passed to {@link CBasePager pagers} + * for corresponding rendering. + * + * Note: this method has been deprecated since version 1.0.1. + * You should directly use "new CPagination" to create a pagination object. + * + * @param integer the total item count + * @param integer the page size. See {@link CPagination} for default value. + * @param string the name of the GET variable storing the current page index. See {@link CPagination} for default value. + * @return CPagination the pagination information + */ + public function paginate($itemCount,$pageSize=null,$pageVar=null) + { + $pages=new CPagination($itemCount); + if($pageSize!==null) + $pages->pageSize=$pageSize; + if($pageVar!==null) + $pages->pageVar=$pageVar; + return $pages; + } + + /** + * Returns a persistent page state value. + * A page state is a variable that is persistent across POST requests of the same page. + * In order to use persistent page states, the form(s) must be stateful + * which are generated using {@link CHtml::statefulForm}. + * @param string the state name + * @param mixed the value to be returned if the named state is not found + * @return mixed the page state value + * @see setPageState + * @see CHtml::statefulForm + */ + public function getPageState($name,$defaultValue=null) + { + if($this->_pageStates===null) + $this->_pageStates=$this->loadPageStates(); + return isset($this->_pageStates[$name])?$this->_pageStates[$name]:$defaultValue; + } + + /** + * Saves a persistent page state value. + * A page state is a variable that is persistent across POST requests of the same page. + * In order to use persistent page states, the form(s) must be stateful + * which are generated using {@link CHtml::statefulForm}. + * @param string the state name + * @param mixed the page state value + * @param mixed the default page state value. If this is the same as + * the given value, the state will be removed from persistent storage. + * @see getPageState + * @see CHtml::statefulForm + */ + public function setPageState($name,$value,$defaultValue=null) + { + if($this->_pageStates===null) + $this->_pageStates=$this->loadPageStates(); + if($value===$defaultValue) + unset($this->_pageStates[$name]); + else + $this->_pageStates[$name]=$value; + + $params=func_get_args(); + $this->recordCachingAction('','setPageState',$params); + } + + /** + * Removes all page states. + */ + public function clearPageStates() + { + $this->_pageStates=array(); + } + + /** + * Loads page states from a hidden input. + * @return array the loaded page states + */ + protected function loadPageStates() + { + if(isset($_POST[self::STATE_INPUT_NAME]) && !empty($_POST[self::STATE_INPUT_NAME])) + { + if(($data=base64_decode($_POST[self::STATE_INPUT_NAME]))!==false) + { + if(extension_loaded('zlib')) + $data=@gzuncompress($data); + if(($data=Yii::app()->getSecurityManager()->validateData($data))!==false) + return unserialize($data); + } + } + return array(); + } + + /** + * Saves page states as a base64 string. + * @param array the states to be saved. + * @param string the output to be modified. Note, this is passed by reference. + */ + protected function savePageStates($states,&$output) + { + $data=Yii::app()->getSecurityManager()->hashData(serialize($states)); + if(extension_loaded('zlib')) + $data=gzcompress($data); + $value=base64_encode($data); + $output=str_replace(CHtml::pageStateField(''),CHtml::pageStateField($value),$output); + } +} diff --git a/web/framework-1.0.8/web/CDbHttpSession.php b/web/framework-1.0.8/web/CDbHttpSession.php new file mode 100644 index 0000000..63f6b3e --- /dev/null +++ b/web/framework-1.0.8/web/CDbHttpSession.php @@ -0,0 +1,203 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbHttpSession extends {@link CHttpSession} by using database as session data storage. + * + * CDbHttpSession stores session data in a DB table named 'YiiSession'. The table name + * can be changed by setting {@link sessionTableName}. If the table does not exist, + * it will be automatically created if {@link autoCreateSessionTable} is set true. + * + * CDbHttpSession relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database. + * + * By default, it will use an SQLite3 database named 'session-YiiVersion.db' under the application runtime directory. + * You can also specify {@link connectionID} so that it makes use of a DB application component to access database. + * + * @author Qiang Xue + * @version $Id: CDbHttpSession.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CDbHttpSession extends CHttpSession +{ + /** + * @var string the ID of a {@link CDbConnection} application component. If not set, a SQLite database + * will be automatically created and used. The SQLite database file is + * is protected/runtime/session-YiiVersion.db. + */ + public $connectionID; + /** + * @var string the name of the DB table to store session content. + * Note, if {@link autoCreateSessionTable} is false and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + *
    +	 * (id CHAR(32) PRIMARY KEY, expire INTEGER, data TEXT)
    +	 * 
    + * @see autoCreateSessionTable + */ + public $sessionTableName='YiiSession'; + /** + * @var boolean whether the session DB table should be automatically created if not exists. Defaults to true. + * @see sessionTableName + */ + public $autoCreateSessionTable=true; + /** + * @var CDbConnection the DB connection instance + */ + private $_db; + + + /** + * Returns a value indicating whether to use custom session storage. + * This method overrides the parent implementation and always returns true. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return true; + } + + /** + * Creates the session DB table. + * @param CDbConnection the database connection + * @param string the name of the table to be created + */ + protected function createSessionTable($db,$tableName) + { + $sql=" +CREATE TABLE $tableName +( + id CHAR(32) PRIMARY KEY, + expire INTEGER, + data TEXT +)"; + $db->createCommand($sql)->execute(); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($id=$this->connectionID)!==null) + { + if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$id))); + } + else + { + $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'session-'.Yii::getVersion().'.db'; + return $this->_db=new CDbConnection('sqlite:'.$dbFile); + } + } + + /** + * Session open handler. + * Do not call this method directly. + * @param string session save path + * @param string session name + * @return boolean whether session is opened successfully + */ + public function openSession($savePath,$sessionName) + { + $db=$this->getDbConnection(); + $db->setActive(true); + + if($this->autoCreateSessionTable) + { + $sql="DELETE FROM {$this->sessionTableName} WHERE expire<".time(); + try + { + $db->createCommand($sql)->execute(); + } + catch(Exception $e) + { + $this->createSessionTable($db,$this->sessionTableName); + } + } + return true; + } + + /** + * Session read handler. + * Do not call this method directly. + * @param string session ID + * @return string the session data + */ + public function readSession($id) + { + $now=time(); + $id=md5($id); + $sql=" +SELECT data FROM {$this->sessionTableName} +WHERE expire>$now AND id='$id' +"; + $data=$this->getDbConnection()->createCommand($sql)->queryScalar(); + return $data===false?'':$data; + } + + /** + * Session write handler. + * Do not call this method directly. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function writeSession($id,$data) + { + $expire=time()+$this->getTimeout(); + $id=md5($id); + $db=$this->getDbConnection(); + $sql="SELECT id FROM {$this->sessionTableName} WHERE id='$id'"; + if($db->createCommand($sql)->queryScalar()===false) + $sql="INSERT INTO {$this->sessionTableName} (id, data, expire) VALUES ('$id', :data, $expire)"; + else + $sql="UPDATE {$this->sessionTableName} SET expire=$expire, data=:data WHERE id='$id'"; + $command=$db->createCommand($sql); + $command->bindParam(':data',$data); + $command->execute(); + return true; + } + + /** + * Session destroy handler. + * Do not call this method directly. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + $id=md5($id); + $sql="DELETE FROM {$this->sessionTableName} WHERE id='$id'"; + $this->getDbConnection()->createCommand($sql)->execute(); + return true; + } + + /** + * Session GC (garbage collection) handler. + * Do not call this method directly. + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function gcSession($maxLifetime) + { + $db=$this->getDbConnection(); + $db->setActive(true); + $sql="DELETE FROM {$this->sessionTableName} WHERE expire<".time(); + $db->createCommand($sql)->execute(); + return true; + } +} diff --git a/web/framework-1.0.8/web/CExtController.php b/web/framework-1.0.8/web/CExtController.php new file mode 100644 index 0000000..4dc46c1 --- /dev/null +++ b/web/framework-1.0.8/web/CExtController.php @@ -0,0 +1,53 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CExtController is the base class for controllers distributed as extension. + * + * The main purpose of CExtController is to redefine the {@link viewPath} property + * so that it points to the "views" subdirectory under the directory containing + * the controller class file. + * + * @author Qiang Xue + * @version $Id: CExtController.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CExtController extends CController +{ + private $_viewPath; + + /** + * Returns the directory containing view files for this controller. + * This method overrides the parent implementation by specifying the view path + * to be the "views" subdirectory under the directory containing the controller + * class file. + * @return string the directory containing the view files for this controller. + */ + public function getViewPath() + { + if($this->_viewPath===null) + { + $class=new ReflectionClass(get_class($this)); + $this->_viewPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.'views'; + } + return $this->_viewPath; + } + + /** + * @param string the directory containing the view files for this controller. + * @since 1.0.1 + */ + public function setViewPath($value) + { + $this->_viewPath=$value; + } +} diff --git a/web/framework-1.0.8/web/CFormModel.php b/web/framework-1.0.8/web/CFormModel.php new file mode 100644 index 0000000..995ca59 --- /dev/null +++ b/web/framework-1.0.8/web/CFormModel.php @@ -0,0 +1,113 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFormModel represents a data model that collects HTML form inputs. + * + * Unlike {@link CActiveRecord}, the data collected by CFormModel are stored + * in memory only, instead of database. + * + * To collect user inputs, you may extend CFormModel and define the attributes + * whose values are to be collected from user inputs. You may override + * {@link rules()} to declare validation rules that should be applied to + * the attributes. + * + * @author Qiang Xue + * @version $Id: CFormModel.php 1243 2009-07-13 01:47:01Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CFormModel extends CModel +{ + private $_validators; + private static $_names=array(); + + /** + * Constructor. + * @param array initial attributes (name => value). The attributes + * are subject to filtering via {@link setAttributes}. + * @param string scenario name. See {@link setAttributes} for more details about this parameter. + * This parameter has been available since version 1.0.2. + * As of version 1.0.4, this parameter will be used to set the {@link CModel::scenario scenario} + * property of the model. + * @see setAttributes + */ + public function __construct($attributes=array(),$scenario='') + { + $this->setScenario($scenario); + + $this->init(); + + if($attributes!==array()) + $this->setAttributes($attributes); + $this->attachBehaviors($this->behaviors()); + } + + /** + * Initializes this model. + * This method is invoked in the constructor right after {@link scenario} is set. + * You may override this method to provide code that is needed to initialize the model (e.g. setting + * initial property values.) + * @since 1.0.8 + */ + public function init() + { + } + + /** + * Returns the name of attributes that are safe to be massively assigned. + * The default implementation simply returns {@link attributeNames}. + * This method may be overridden by child classes. + * See {@link CModel::safeAttributes} for more details about how to + * override this method. + * @return array list of safe attribute names. + * @see CModel::safeAttributes + * @since 1.0.2 + */ + public function safeAttributes() + { + return $this->attributeNames(); + } + + /** + * Returns the list of attribute names. + * By default, this method returns all public properties of the class. + * You may override this method to change the default. + * @return array list of attribute names. Defaults to all public properties of the class. + */ + public function attributeNames() + { + $className=get_class($this); + if(!isset(self::$_names[$className])) + { + $class=new ReflectionClass(get_class($this)); + $names=array(); + foreach($class->getProperties() as $property) + { + $name=$property->getName(); + if($property->isPublic() && !$property->isStatic()) + $names[]=$name; + } + return self::$_names[$className]=$names; + } + else + return self::$_names[$className]; + } + + /** + * @return array list of validators created according to {@link CModel::rules rules}. + */ + public function getValidators() + { + if($this->_validators===null) + $this->_validators=$this->createValidators(); + return $this->_validators; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/CHttpCookie.php b/web/framework-1.0.8/web/CHttpCookie.php new file mode 100644 index 0000000..f4fd7fe --- /dev/null +++ b/web/framework-1.0.8/web/CHttpCookie.php @@ -0,0 +1,63 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * A CHttpCookie instance stores a single cookie, including the cookie name, value, domain, path, expire, and secure. + * + * @author Qiang Xue + * @version $Id: CHttpCookie.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CHttpCookie extends CComponent +{ + /** + * @var string name of the cookie + */ + public $name; + /** + * @var string value of the cookie + */ + public $value=''; + /** + * @var string domain of the cookie + */ + public $domain=''; + /** + * @var integer the timestamp at which the cookie expires. This is the server timestamp. Defaults to 0, meaning "until the browser is closed". + */ + public $expire=0; + /** + * @var string the path on the server in which the cookie will be available on. The default is '/'. + */ + public $path='/'; + /** + * @var boolean whether cookie should be sent via secure connection + */ + public $secure=false; + /** + * @var boolean whether the cookie should be accessible only through the HTTP protocol. + * By setting this property to true, the cookie will not be accessible by scripting languages, + * such as JavaScript, which can effectly help to reduce identity theft through XSS attacks. + * Note, this property is only effective for PHP 5.2.0 or above. + */ + public $httpOnly=true; + + /** + * Constructor. + * @param string name of this cookie + * @param string value of this cookie + */ + public function __construct($name,$value) + { + $this->name=$name; + $this->value=$value; + } +} diff --git a/web/framework-1.0.8/web/CHttpRequest.php b/web/framework-1.0.8/web/CHttpRequest.php new file mode 100644 index 0000000..5dfc29d --- /dev/null +++ b/web/framework-1.0.8/web/CHttpRequest.php @@ -0,0 +1,763 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CHttpRequest encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers. + * + * CHttpRequest also manages the cookies sent from and sent to the user. + * By setting {@link enableCookieValidation} to true, + * cookies sent from the user will be validated to see if they are tampered. + * The property {@link getCookies cookies} returns the collection of cookies. + * For more details, see {@link CCookieCollection}. + * + * CHttpRequest is a default application component loaded by {@link CWebApplication}. It can be + * accessed via {@link CWebApplication::getRequest()}. + * + * @author Qiang Xue + * @version $Id: CHttpRequest.php 1283 2009-08-01 01:21:38Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CHttpRequest extends CApplicationComponent +{ + /** + * @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to false. + */ + public $enableCookieValidation=false; + /** + * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false. + * By setting this property to true, forms submitted to an Yii Web application must be originated + * from the same application. If not, a 400 HTTP exception will be raised. + * Note, this feature requires that the user client accepts cookie. + * You also need to use {@link CHtml::form} or {@link CHtml::statefulForm} to generate + * the needed HTML forms in your pages. + * @see http://freedom-to-tinker.com/sites/default/files/csrf.pdf + */ + public $enableCsrfValidation=false; + /** + * @var string the name of the token used to prevent CSRF. Defaults to 'YII_CSRF_TOKEN'. + * This property is effectively only when {@link enableCsrfValidation} is true. + */ + public $csrfTokenName='YII_CSRF_TOKEN'; + /** + * @var array the property values (in name-value pairs) used to initialize the CSRF cookie. + * Any property of {@link CHttpCookie} may be initialized. + * This property is effective only when {@link enableCsrfValidation} is true. + */ + public $csrfCookie; + + private $_requestUri; + private $_pathInfo; + private $_scriptFile; + private $_scriptUrl; + private $_hostInfo; + private $_url; + private $_baseUrl; + private $_cookies; + private $_preferredLanguage; + private $_csrfToken; + + /** + * Initializes the application component. + * This method overrides the parent implementation by preprocessing + * the user request data. + */ + public function init() + { + parent::init(); + $this->normalizeRequest(); + } + + /** + * Normalizes the request data. + * This method strips off slashes in request data if get_magic_quotes_gpc() returns true. + * It also performs CSRF validation if {@link enableCsrfValidation} is true. + */ + protected function normalizeRequest() + { + // normalize request + if(get_magic_quotes_gpc()) + { + if(isset($_GET)) + $_GET=$this->stripSlashes($_GET); + if(isset($_POST)) + $_POST=$this->stripSlashes($_POST); + if(isset($_REQUEST)) + $_REQUEST=$this->stripSlashes($_REQUEST); + if(isset($_COOKIE)) + $_COOKIE=$this->stripSlashes($_COOKIE); + } + + if($this->enableCsrfValidation) + Yii::app()->attachEventHandler('onBeginRequest',array($this,'validateCsrfToken')); + } + + + /** + * Strips slashes from input data. + * This method is applied when magic quotes is enabled. + * @param mixed input data to be processed + * @return mixed processed data + */ + public function stripSlashes(&$data) + { + return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data); + } + + /** + * Returns the named GET or POST parameter value. + * If the GET or POST parameter does not exist, the second parameter to this method will be returned. + * If both GET and POST contains such a named parameter, the GET parameter takes precedence. + * @param string the GET parameter name + * @param mixed the default parameter value if the GET parameter does not exist. + * @return mixed the GET parameter value + * @since 1.0.4 + * @see getQuery + * @see getPost + */ + public function getParam($name,$defaultValue=null) + { + return isset($_GET[$name]) ? $_GET[$name] : (isset($_POST[$name]) ? $_POST[$name] : $defaultValue); + } + + /** + * Returns the named GET parameter value. + * If the GET parameter does not exist, the second parameter to this method will be returned. + * @param string the GET parameter name + * @param mixed the default parameter value if the GET parameter does not exist. + * @return mixed the GET parameter value + * @since 1.0.4 + * @see getPost + * @see getParam + */ + public function getQuery($name,$defaultValue=null) + { + return isset($_GET[$name]) ? $_GET[$name] : $defaultValue; + } + + /** + * Returns the named POST parameter value. + * If the POST parameter does not exist, the second parameter to this method will be returned. + * @param string the POST parameter name + * @param mixed the default parameter value if the POST parameter does not exist. + * @return mixed the POST parameter value + * @since 1.0.4 + * @see getParam + * @see getQuery + */ + public function getPost($name,$defaultValue=null) + { + return isset($_POST[$name]) ? $_POST[$name] : $defaultValue; + } + + /** + * @return string part of the request URL after the host info. + * It consists of the following parts: + *
      + *
    • {@link getScriptUrl scriptUrl}
    • + *
    • {@link getPathInfo pathInfo}
    • + *
    • {@link getQueryString queryString}
    • + *
    + */ + public function getUrl() + { + if($this->_url!==null) + return $this->_url; + else + { + if(isset($_SERVER['REQUEST_URI'])) + $this->_url=$_SERVER['REQUEST_URI']; + else + { + $this->_url=$this->getScriptUrl(); + if(($pathInfo=$this->getPathInfo())!=='') + $this->_url.='/'.$pathInfo; + if(($queryString=$this->getQueryString())!=='') + $this->_url.='?'.$queryString; + } + return $this->_url; + } + } + + /** + * Returns the schema and host part of the application URL. + * The returned URL does not have an ending slash. + * By default this is determined based on the user request information. + * You may explicitly specify it by setting the {@link setHostInfo hostInfo} property. + * @param string schema to use (e.g. http, https). If empty, the schema used for the current request will be used. + * @return string schema and hostname part (with port number if needed) of the request URL (e.g. http://www.yiiframework.com) + * @see setHostInfo + */ + public function getHostInfo($schema='') + { + if($this->_hostInfo===null) + { + if($secure=$this->getIsSecureConnection()) + $http='https'; + else + $http='http'; + if(isset($_SERVER['HTTP_HOST'])) + $this->_hostInfo=$http.'://'.$_SERVER['HTTP_HOST']; + else + { + $this->_hostInfo=$http.'://'.$_SERVER['SERVER_NAME']; + $port=$_SERVER['SERVER_PORT']; + if(($port!=80 && !$secure) || ($port!=443 && $secure)) + $this->_hostInfo.=':'.$port; + } + } + if($schema!=='' && ($pos=strpos($this->_hostInfo,':'))!==false) + return $schema.substr($this->_hostInfo,$pos); + else + return $this->_hostInfo; + } + + /** + * Sets the schema and host part of the application URL. + * This setter is provided in case the schema and hostname cannot be determined + * on certain Web servers. + * @param string the schema and host part of the application URL. + */ + public function setHostInfo($value) + { + $this->_hostInfo=rtrim($value,'/'); + } + + /** + * Returns the relative URL for the application. + * This is similar to {@link getScriptUrl scriptUrl} except that + * it does not have the script file name, and the ending slashes are stripped off. + * @param boolean whether to return an absolute URL. Defaults to false, meaning returning a relative one. + * This parameter has been available since 1.0.2. + * @return string the relative URL for the application + * @see setScriptUrl + */ + public function getBaseUrl($absolute=false) + { + if($this->_baseUrl===null) + $this->_baseUrl=rtrim(dirname($this->getScriptUrl()),'\\/'); + return $absolute ? $this->getHostInfo() . $this->_baseUrl : $this->_baseUrl; + } + + /** + * Sets the relative URL for the application. + * By default the URL is determined based on the entry script URL. + * This setter is provided in case you want to change this behavior. + * @param string the relative URL for the application + */ + public function setBaseUrl($value) + { + $this->_baseUrl=$value; + } + + /** + * Returns the relative URL of the entry script. + * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework. + * @return string the relative URL of the entry script. + */ + public function getScriptUrl() + { + if($this->_scriptUrl===null) + { + $scriptName=basename($_SERVER['SCRIPT_FILENAME']); + if(basename($_SERVER['SCRIPT_NAME'])===$scriptName) + $this->_scriptUrl=$_SERVER['SCRIPT_NAME']; + else if(basename($_SERVER['PHP_SELF'])===$scriptName) + $this->_scriptUrl=$_SERVER['PHP_SELF']; + else if(isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME'])===$scriptName) + $this->_scriptUrl=$_SERVER['ORIG_SCRIPT_NAME']; + else if(($pos=strpos($_SERVER['PHP_SELF'],'/'.$scriptName))!==false) + $this->_scriptUrl=substr($_SERVER['SCRIPT_NAME'],0,$pos).'/'.$scriptName; + else if(isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'],$_SERVER['DOCUMENT_ROOT'])===0) + $this->_scriptUrl=str_replace('\\','/',str_replace($_SERVER['DOCUMENT_ROOT'],'',$_SERVER['SCRIPT_FILENAME'])); + else + throw new CException(Yii::t('yii','CHttpRequest is unable to determine the entry script URL.')); + } + return $this->_scriptUrl; + } + + /** + * Sets the relative URL for the application entry script. + * This setter is provided in case the entry script URL cannot be determined + * on certain Web servers. + * @param string the relative URL for the application entry script. + */ + public function setScriptUrl($value) + { + $this->_scriptUrl='/'.trim($value,'/'); + } + + /** + * Returns the path info of the currently requested URL. + * This refers to the part that is after the entry script and before the question mark. + * The starting and ending slashes are stripped off. + * @return string part of the request URL that is after the entry script and before the question mark. + * @throws CException if the request URI cannot be determined due to improper server configuration + */ + public function getPathInfo() + { + if($this->_pathInfo===null) + { + $requestUri=$this->getRequestUri(); + $scriptUrl=$this->getScriptUrl(); + $baseUrl=$this->getBaseUrl(); + if(strpos($requestUri,$scriptUrl)===0) + $pathInfo=substr($requestUri,strlen($scriptUrl)); + else if($baseUrl==='' || strpos($requestUri,$baseUrl)===0) + $pathInfo=substr($requestUri,strlen($baseUrl)); + else if(strpos($_SERVER['PHP_SELF'],$scriptUrl)===0) + $pathInfo=substr($_SERVER['PHP_SELF'],strlen($scriptUrl)); + else + throw new CException(Yii::t('yii','CHttpRequest is unable to determine the path info of the request.')); + + if(($pos=strpos($pathInfo,'?'))!==false) + $pathInfo=substr($pathInfo,0,$pos); + + $this->_pathInfo=trim($pathInfo,'/'); + } + return $this->_pathInfo; + } + + /** + * Returns the request URI portion for the currently requested URL. + * This refers to the portion that is after the {@link hostInfo host info} part. + * It includes the {@link queryString query string} part if any. + * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework. + * @return string the request URI portion for the currently requested URL. + * @throws CException if the request URI cannot be determined due to improper server configuration + * @since 1.0.1 + */ + public function getRequestUri() + { + if($this->_requestUri===null) + { + if(isset($_SERVER['HTTP_X_REWRITE_URL'])) // IIS + $this->_requestUri=$_SERVER['HTTP_X_REWRITE_URL']; + else if(isset($_SERVER['REQUEST_URI'])) + { + $this->_requestUri=$_SERVER['REQUEST_URI']; + if(strpos($this->_requestUri,$_SERVER['HTTP_HOST'])!==false) + $this->_requestUri=preg_replace('/^\w+:\/\/[^\/]+/','',$this->_requestUri); + } + else if(isset($_SERVER['ORIG_PATH_INFO'])) // IIS 5.0 CGI + { + $this->_requestUri=$_SERVER['ORIG_PATH_INFO']; + if(!empty($_SERVER['QUERY_STRING'])) + $this->_requestUri.='?'.$_SERVER['QUERY_STRING']; + } + else + throw new CException(Yii::t('yii','CHttpRequest is unable to determine the request URI.')); + } + + return $this->_requestUri; + } + + /** + * @return string part of the request URL that is after the question mark + */ + public function getQueryString() + { + return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:''; + } + + /** + * @return boolean if the request is sent via secure channel (https) + */ + public function getIsSecureConnection() + { + return isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'],'on'); + } + + /** + * @return string request type, such as GET, POST, HEAD, PUT, DELETE. + */ + public function getRequestType() + { + return strtoupper(isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'GET'); + } + + /** + * @return boolean whether this is POST request. + */ + public function getIsPostRequest() + { + return !strcasecmp($_SERVER['REQUEST_METHOD'],'POST'); + } + + /** + * @return boolean whether this is an AJAX (XMLHttpRequest) request. + */ + public function getIsAjaxRequest() + { + return isset($_SERVER['HTTP_X_REQUESTED_WITH'])?$_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest' : false; + } + + /** + * @return string server name + */ + public function getServerName() + { + return $_SERVER['SERVER_NAME']; + } + + /** + * @return integer server port number + */ + public function getServerPort() + { + return $_SERVER['SERVER_PORT']; + } + + /** + * @return string URL referrer, null if not present + */ + public function getUrlReferrer() + { + return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null; + } + + /** + * @return string user agent + */ + public function getUserAgent() + { + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * @return string user IP address + */ + public function getUserHostAddress() + { + return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1'; + } + + /** + * @return string user host name, null if cannot be determined + */ + public function getUserHost() + { + return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null; + } + + /** + * @return string entry script file path (processed w/ realpath()) + */ + public function getScriptFile() + { + if($this->_scriptFile!==null) + return $this->_scriptFile; + else + return $this->_scriptFile=realpath($_SERVER['SCRIPT_FILENAME']); + } + + /** + * Returns information about the capabilities of user browser. + * @param string the user agent to be analyzed. Defaults to null, meaning using the + * current User-Agent HTTP header information. + * @return array user browser capabilities. + * @see http://www.php.net/manual/en/function.get-browser.php + */ + public function getBrowser($userAgent=null) + { + return get_browser($userAgent,true); + } + + /** + * @return string user browser accept types + */ + public function getAcceptTypes() + { + return $_SERVER['HTTP_ACCEPT']; + } + + /** + * Returns the cookie collection. + * The result can be used like an associative array. Adding {@link CHttpCookie} objects + * to the collection will send the cookies to the client; and removing the objects + * from the collection will delete those cookies on the client. + * @return CCookieCollection the cookie collection. + */ + public function getCookies() + { + if($this->_cookies!==null) + return $this->_cookies; + else + return $this->_cookies=new CCookieCollection($this); + } + + /** + * Redirects the browser to the specified URL. + * @param string URL to be redirected to. If the URL is a relative one, the base URL of + * the application will be inserted at the beginning. + * @param boolean whether to terminate the current application + * @param integer the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} + * for details about HTTP status code. This parameter has been available since version 1.0.4. + */ + public function redirect($url,$terminate=true,$statusCode=302) + { + if(strpos($url,'/')===0) + $url=$this->getHostInfo().$url; + header('Location: '.$url, true, $statusCode); + if($terminate) + Yii::app()->end(); + } + + /** + * @return string the user preferred language. + * The returned language ID will be canonicalized using {@link CLocale::getCanonicalID}. + * This method returns false if the user does not have language preference. + */ + public function getPreferredLanguage() + { + if($this->_preferredLanguage===null) + { + if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && ($n=preg_match_all('/([\w\-_]+)\s*(;\s*q\s*=\s*(\d*\.\d*))?/',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches))>0) + { + $languages=array(); + for($i=0;$i<$n;++$i) + $languages[$matches[1][$i]]=empty($matches[3][$i]) ? 1.0 : floatval($matches[3][$i]); + arsort($languages); + foreach($languages as $language=>$pref) + return $this->_preferredLanguage=CLocale::getCanonicalID($language); + } + return $this->_preferredLanguage=false; + } + return $this->_preferredLanguage; + } + + /** + * Sends a file to user. + * @param string file name + * @param string content to be set. + * @param string mime type of the content. If null, it will be guessed automatically based on the given file name. + * @param boolean whether to terminate the current application after calling this method + */ + public function sendFile($fileName,$content,$mimeType=null,$terminate=true) + { + if($mimeType===null) + { + if(($mimeType=CFileHelper::getMimeTypeByExtension($fileName))===null) + $mimeType='text/plain'; + } + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-type: $mimeType"); + header('Content-Length: '.strlen($content)); + header("Content-Disposition: attachment; filename=\"$fileName\""); + header('Content-Transfer-Encoding: binary'); + echo $content; + if($terminate) + Yii::app()->end(); + } + + /** + * Returns the random token used to perform CSRF validation. + * The token will be read from cookie first. If not found, a new token + * will be generated. + * @return string the random token for CSRF validation. + * @see enableCsrfValidation + */ + public function getCsrfToken() + { + if($this->_csrfToken===null) + { + $cookie=$this->getCookies()->itemAt($this->csrfTokenName); + if(!$cookie || ($this->_csrfToken=$cookie->value)==null) + { + $cookie=$this->createCsrfCookie(); + $this->_csrfToken=$cookie->value; + $this->getCookies()->add($cookie->name,$cookie); + } + } + + return $this->_csrfToken; + } + + /** + * Creates a cookie with a randomly generated CSRF token. + * Initial values specified in {@link csrfCookie} will be applied + * to the generated cookie. + * @return CHttpCookie the generated cookie + * @see enableCsrfValidation + */ + protected function createCsrfCookie() + { + $cookie=new CHttpCookie($this->csrfTokenName,sha1(uniqid(rand(),true))); + if(is_array($this->csrfCookie)) + { + foreach($this->csrfCookie as $name=>$value) + $cookie->$name=$value; + } + return $cookie; + } + + /** + * Performs the CSRF validation. + * This is the event handler responding to {@link CApplication::onBeginRequest}. + * The default implementation will compare the CSRF token obtained + * from a cookie and from a POST field. If they are different, a CSRF attack is detected. + * @param CEvent event parameter + * @throws CHttpException if the validation fails + * @since 1.0.4 + */ + public function validateCsrfToken($event) + { + if($this->getIsPostRequest()) + { + // only validate POST requests + $cookies=$this->getCookies(); + if($cookies->contains($this->csrfTokenName) && isset($_POST[$this->csrfTokenName])) + { + $tokenFromCookie=$cookies->itemAt($this->csrfTokenName)->value; + $tokenFromPost=$_POST[$this->csrfTokenName]; + $valid=$tokenFromCookie===$tokenFromPost; + } + else + $valid=false; + if(!$valid) + throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.')); + } + } +} + + +/** + * CCookieCollection implements a collection class to store cookies. + * + * You normally access it via {@link CHttpRequest::getCookies()}. + * + * Since CCookieCollection extends from {@link CMap}, it can be used + * like an associative array as follows: + *
    + * $cookies[$name]=new CHttpCookie($name,$value); // sends a cookie
    + * $value=$cookies[$name]->value; // reads a cookie value
    + * unset($cookies[$name]);  // removes a cookie
    + * 
    + * + * @author Qiang Xue + * @version $Id: CHttpRequest.php 1283 2009-08-01 01:21:38Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CCookieCollection extends CMap +{ + private $_request; + private $_initialized=false; + + /** + * Constructor. + * @param CHttpRequest owner of this collection. + */ + public function __construct(CHttpRequest $request) + { + $this->_request=$request; + $this->copyfrom($this->getCookies()); + $this->_initialized=true; + } + + /** + * @return CHttpRequest the request instance + */ + public function getRequest() + { + return $this->_request; + } + + /** + * @return array list of validated cookies + */ + protected function getCookies() + { + $cookies=array(); + if($this->_request->enableCookieValidation) + { + $sm=Yii::app()->getSecurityManager(); + foreach($_COOKIE as $name=>$value) + { + if(($value=$sm->validateData($value))!==false) + $cookies[$name]=new CHttpCookie($name,$value); + } + } + else + { + foreach($_COOKIE as $name=>$value) + $cookies[$name]=new CHttpCookie($name,$value); + } + return $cookies; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added CHttpCookie object. + * @param integer the specified position. + * @param mixed new item + * @throws CException if the item to be inserted is not a CHttpCookie object. + */ + public function add($name,$cookie) + { + if($cookie instanceof CHttpCookie) + { + $this->remove($name); + parent::add($name,$cookie); + if($this->_initialized) + $this->addCookie($cookie); + } + else + throw new CException(Yii::t('yii','CHttpCookieCollection can only hold CHttpCookie objects.')); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a TCookie object. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function remove($name) + { + if(($cookie=parent::remove($name))!==null) + { + if($this->_initialized) + $this->removeCookie($cookie); + } + return $cookie; + } + + /** + * Sends a cookie. + * @param CHttpCookie cook to be sent + */ + protected function addCookie($cookie) + { + $value=$cookie->value; + if($this->_request->enableCookieValidation) + $value=Yii::app()->getSecurityManager()->hashData($value); + if(version_compare(PHP_VERSION,'5.2.0','>=')) + setcookie($cookie->name,$value,$cookie->expire,$cookie->path,$cookie->domain,$cookie->secure,$cookie->httpOnly); + else + setcookie($cookie->name,$value,$cookie->expire,$cookie->path,$cookie->domain,$cookie->secure); + } + + /** + * Deletes a cookie. + * @param CHttpCookie cook to be deleted + */ + protected function removeCookie($cookie) + { + if(version_compare(PHP_VERSION,'5.2.0','>=')) + setcookie($cookie->name,null,0,$cookie->path,$cookie->domain,$cookie->secure,$cookie->httpOnly); + else + setcookie($cookie->name,null,0,$cookie->path,$cookie->domain,$cookie->secure); + } +} diff --git a/web/framework-1.0.8/web/CHttpSession.php b/web/framework-1.0.8/web/CHttpSession.php new file mode 100644 index 0000000..e1c49ef --- /dev/null +++ b/web/framework-1.0.8/web/CHttpSession.php @@ -0,0 +1,522 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CHttpSession provides session-level data management and the related configurations. + * + * To start the session, call {@link open()}; To complete and send out session data, call {@link close()}; + * To destroy the session, call {@link destroy()}. + * + * If {@link autoStart} is set true, the session will be started automatically + * when the application component is initialized by the application. + * + * CHttpSession can be used like an array to set and get session data. For example, + *
    + *   $session=new CHttpSession;
    + *   $session->open();
    + *   $value1=$session['name1'];  // get session variable 'name1'
    + *   $value2=$session['name2'];  // get session variable 'name2'
    + *   foreach($session as $name=>$value) // traverse all session variables
    + *   $session['name3']=$value3;  // set session variable 'name3'
    + * 
    + * + * The following configurations are available for session: + *
      + *
    • {@link setSessionID sessionID};
    • + *
    • {@link setSessionName sessionName};
    • + *
    • {@link autoStart};
    • + *
    • {@link setSavePath savePath};
    • + *
    • {@link setCookieParams cookieParams};
    • + *
    • {@link setGCProbability gcProbability};
    • + *
    • {@link setCookieMode cookieMode};
    • + *
    • {@link setUseTransparentSessionID useTransparentSessionID};
    • + *
    • {@link setTimeout timeout}.
    • + *
    + * See the corresponding setter and getter documentation for more information. + * Note, these properties must be set before the session is started. + * + * CHttpSession can be extended to support customized session storage. + * Override {@link openSession}, {@link closeSession}, {@link readSession}, + * {@link writeSession}, {@link destroySession} and {@link gcSession} + * and set {@link useCustomStorage} to true. + * Then, the session data will be stored and retrieved using the above methods. + * + * CHttpSession is a Web application component that can be accessed via + * {@link CWebApplication::getSession()}. + * + * @author Qiang Xue + * @version $Id: CHttpSession.php 967 2009-04-29 12:30:14Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CHttpSession extends CApplicationComponent implements IteratorAggregate,ArrayAccess,Countable +{ + /** + * @var boolean whether the session should be automatically started when the session application component is initialized, defaults to true. + */ + public $autoStart=true; + + + /** + * Initializes the application component. + * This method is required by IApplicationComponent and is invoked by application. + */ + public function init() + { + parent::init(); + if($this->autoStart) + $this->open(); + register_shutdown_function(array($this,'close')); + } + + /** + * Returns a value indicating whether to use custom session storage. + * This method should be overriden to return true if custom session storage handler should be used. + * If returning true, make sure the methods {@link openSession}, {@link closeSession}, {@link readSession}, + * {@link writeSession}, {@link destroySession}, and {@link gcSession} are overridden in child + * class, because they will be used as the callback handlers. + * The default implementation always return false. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return false; + } + + /** + * Starts the session if it has not started yet. + */ + public function open() + { + if(session_id()==='') + { + if($this->getUseCustomStorage()) + session_set_save_handler(array($this,'openSession'),array($this,'closeSession'),array($this,'readSession'),array($this,'writeSession'),array($this,'destroySession'),array($this,'gcSession')); + @session_start(); + } + } + + /** + * Ends the current session and store session data. + */ + public function close() + { + if(session_id()!=='') + @session_write_close(); + } + + /** + * Frees all session variables and destroys all data registered to a session. + */ + public function destroy() + { + if(session_id()!=='') + { + @session_unset(); + @session_destroy(); + setcookie(session_name(),'',0,'/'); + session_regenerate_id(true); + } + } + + /** + * @return boolean whether the session has started + */ + public function getIsStarted() + { + return session_id()!==''; + } + + /** + * @return string the current session ID + */ + public function getSessionID() + { + return session_id(); + } + + /** + * @param string the session ID for the current session + */ + public function setSessionID($value) + { + session_id($value); + } + + /** + * @return string the current session name + */ + public function getSessionName() + { + return session_name(); + } + + /** + * @param string the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID + */ + public function setSessionName($value) + { + session_name($value); + } + + /** + * @return string the current session save path, defaults to '/tmp'. + */ + public function getSavePath() + { + return session_save_path(); + } + + /** + * @param string the current session save path + * @throws CException if the path is not a valid directory + */ + public function setSavePath($value) + { + if(is_dir($value)) + session_save_path($value); + else + throw new CException(Yii::t('yii','CHttpSession.savePath "{path}" is not a valid directory.', + array('{path}'=>$value))); + } + + /** + * @return array the session cookie parameters. + * @see http://us2.php.net/manual/en/function.session-get-cookie-params.php + */ + public function getCookieParams() + { + return session_get_cookie_params(); + } + + /** + * Sets the session cookie parameters. + * The effect of this method only lasts for the duration of the script. + * Call this method before the session starts. + * @param array cookie parameters, valid keys include: lifetime, path, domain, secure. + * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php + */ + public function setCookieParams($value) + { + $data=session_get_cookie_params(); + extract($data); + extract($value); + if(isset($httponly)) + session_set_cookie_params($lifetime,$path,$domain,$secure,$httponly); + else + session_set_cookie_params($lifetime,$path,$domain,$secure); + } + + /** + * @return string how to use cookie to store session ID. Defaults to 'Allow'. + */ + public function getCookieMode() + { + if(ini_get('session.use_cookies')==='0') + return 'none'; + else if(ini_get('session.use_only_cookies')==='0') + return 'allow'; + else + return 'only'; + } + + /** + * @param string how to use cookie to store session ID. Valid values include 'none', 'allow' and 'only'. + */ + public function setCookieMode($value) + { + if($value==='none') + ini_set('session.use_cookies','0'); + else if($value==='allow') + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','0'); + } + else if($value==='only') + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','1'); + } + else + throw new CException(Yii::t('yii','CHttpSession.cookieMode can only be "none", "allow" or "only".')); + } + + /** + * @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. + */ + public function getGCProbability() + { + return (int)ini_get('session.gc_probability'); + } + + /** + * @param integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization. + * @throws CException if the value is beyond [0,100] + */ + public function setGCProbability($value) + { + $value=(int)$value; + if($value>=0 && $value<=100) + { + ini_set('session.gc_probability',$value); + ini_set('session.gc_divisor','100'); + } + else + throw new CException(Yii::t('yii','CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.', + array('{value}'=>$value))); + } + + /** + * @return boolean whether transparent sid support is enabled or not, defaults to false. + */ + public function getUseTransparentSessionID() + { + return ini_get('session.use_trans_sid')==1; + } + + /** + * @param boolean whether transparent sid support is enabled or not. + */ + public function setUseTransparentSessionID($value) + { + ini_set('session.use_trans_sid',$value?'1':'0'); + } + + /** + * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. + */ + public function getTimeout() + { + return (int)ini_get('session.gc_maxlifetime'); + } + + /** + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up + */ + public function setTimeout($value) + { + ini_set('session.gc_maxlifetime',$value); + } + + /** + * Session open handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @param string session save path + * @param string session name + * @return boolean whether session is opened successfully + */ + public function openSession($savePath,$sessionName) + { + return true; + } + + /** + * Session close handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @return boolean whether session is closed successfully + */ + public function closeSession() + { + return true; + } + + /** + * Session read handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @param string session ID + * @return string the session data + */ + public function readSession($id) + { + return ''; + } + + /** + * Session write handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function writeSession($id,$data) + { + return true; + } + + /** + * Session destroy handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + return true; + } + + /** + * Session GC (garbage collection) handler. + * This method should be overridden if {@link useCustomStorage} is set true. + * Do not call this method directly. + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function gcSession($maxLifetime) + { + return true; + } + + //------ The following methods enable CHttpSession to be CMap-like ----- + + /** + * Returns an iterator for traversing the session variables. + * This method is required by the interface IteratorAggregate. + * @return CHttpSessionIterator an iterator for traversing the session variables. + */ + public function getIterator() + { + return new CHttpSessionIterator; + } + + /** + * @return integer the number of session variables + */ + public function getCount() + { + return count($_SESSION); + } + + /** + * Returns the number of items in the session. + * This method is required by Countable interface. + * @return integer number of items in the session. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return array the list of session variable names + */ + public function getKeys() + { + return array_keys($_SESSION); + } + + /** + * Returns the session variable value with the session variable name. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the session variable name + * @return mixed the session variable value, null if no such variable exists + */ + public function itemAt($key) + { + return isset($_SESSION[$key]) ? $_SESSION[$key] : null; + } + + /** + * Adds a session variable. + * Note, if the specified name already exists, the old value will be removed first. + * @param mixed session variable name + * @param mixed session variable value + */ + public function add($key,$value) + { + $_SESSION[$key]=$value; + } + + /** + * Removes a session variable. + * @param mixed the name of the session variable to be removed + * @return mixed the removed value, null if no such session variable. + */ + public function remove($key) + { + if(isset($_SESSION[$key])) + { + $value=$_SESSION[$key]; + unset($_SESSION[$key]); + return $value; + } + else + return null; + } + + /** + * Removes all session variables + */ + public function clear() + { + foreach(array_keys($_SESSION) as $key) + unset($_SESSION[$key]); + } + + /** + * @param mixed session variable name + * @return boolean whether there is the named session variable + */ + public function contains($key) + { + return isset($_SESSION[$key]); + } + + /** + * @return array the list of all session variables in array + */ + public function toArray() + { + return $_SESSION; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return isset($_SESSION[$offset]); + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $_SESSION[$offset]=$item; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + unset($_SESSION[$offset]); + } +} diff --git a/web/framework-1.0.8/web/CHttpSessionIterator.php b/web/framework-1.0.8/web/CHttpSessionIterator.php new file mode 100644 index 0000000..608ee63 --- /dev/null +++ b/web/framework-1.0.8/web/CHttpSessionIterator.php @@ -0,0 +1,92 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CHttpSessionIterator implements an interator for {@link CHttpSession}. + * + * It allows CHttpSession to return a new iterator for traversing the session variables. + * + * @author Qiang Xue + * @version $Id: CHttpSessionIterator.php 568 2009-01-23 22:50:14Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CHttpSessionIterator implements Iterator +{ + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct() + { + $this->_keys=array_keys($_SESSION); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key=reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + do + { + $this->_key=next($this->_keys); + } + while(!isset($_SESSION[$this->_key]) && $this->_key!==false); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key!==false; + } +} diff --git a/web/framework-1.0.8/web/COutputEvent.php b/web/framework-1.0.8/web/COutputEvent.php new file mode 100644 index 0000000..16bfc1a --- /dev/null +++ b/web/framework-1.0.8/web/COutputEvent.php @@ -0,0 +1,38 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COutputEvent represents the parameter for events related with output handling. + * + * An event handler may retrieve the captured {@link output} for further processing. + * + * @author Qiang Xue + * @version $Id: COutputEvent.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class COutputEvent extends CEvent +{ + /** + * @var string the output to be processed. The processed output should be stored back to this property. + */ + public $output; + + /** + * Constructor. + * @param mixed sender of the event + * @param string the output to be processed + */ + public function __construct($sender,$output) + { + parent::__construct($sender); + $this->output=$output; + } +} diff --git a/web/framework-1.0.8/web/CPagination.php b/web/framework-1.0.8/web/CPagination.php new file mode 100644 index 0000000..38d401f --- /dev/null +++ b/web/framework-1.0.8/web/CPagination.php @@ -0,0 +1,161 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPagination represents information relevant to pagination. + * + * When data needs to be rendered in multiple pages, we can use CPagination to + * represent information such as {@link getItemCount total item count}, + * {@link getPageSize page size}, {@link getCurrentPage current page}, etc. + * These information can be passed to {@link CBasePager pagers} to render + * pagination buttons or links. + * + * + * @author Qiang Xue + * @version $Id: CPagination.php 562 2009-01-21 15:36:03Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CPagination extends CComponent +{ + /** + * The default page size. + */ + const DEFAULT_PAGE_SIZE=10; + /** + * @var string name of the GET variable storing the current page index. Defaults to 'page'. + */ + public $pageVar='page'; + /** + * @var string the route (controller ID and action ID) for displaying the paged contents. + * Defaults to empty string, meaning using the current route. + */ + public $route=''; + + private $_pageSize=self::DEFAULT_PAGE_SIZE; + private $_itemCount=0; + private $_currentPage; + + /** + * Constructor. + * @param integer total number of items. + * @since 1.0.1 + */ + public function __construct($itemCount=0) + { + $this->setItemCount($itemCount); + } + + /** + * @return integer number of items in each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->_pageSize; + } + + /** + * @param integer number of items in each page + */ + public function setPageSize($value) + { + if(($this->_pageSize=$value)<=0) + $this->_pageSize=self::DEFAULT_PAGE_SIZE; + } + + /** + * @return integer total number of items. Defaults to 0. + */ + public function getItemCount() + { + return $this->_itemCount; + } + + /** + * @param integer total number of items. + */ + public function setItemCount($value) + { + if(($this->_itemCount=$value)<0) + $this->_itemCount=0; + } + + /** + * @return integer number of pages + */ + public function getPageCount() + { + return (int)(($this->_itemCount+$this->_pageSize-1)/$this->_pageSize); + } + + /** + * @param boolean whether to recalculate the current page based on the page size and item count. + * @return integer the zero-based index of the current page. Defaults to 0. + */ + public function getCurrentPage($recalculate=true) + { + if($this->_currentPage===null || $recalculate) + { + if(isset($_GET[$this->pageVar])) + { + $this->_currentPage=(int)$_GET[$this->pageVar]-1; + $pageCount=$this->getPageCount(); + if($this->_currentPage>=$pageCount) + $this->_currentPage=$pageCount-1; + if($this->_currentPage<0) + $this->_currentPage=0; + } + else + $this->_currentPage=0; + } + return $this->_currentPage; + } + + /** + * @param integer the zero-based index of the current page. + */ + public function setCurrentPage($value) + { + $this->_currentPage=$value; + $_GET[$this->pageVar]=$value+1; + } + + /** + * Creates the URL suitable for pagination. + * This method is mainly called by pagers when creating URLs used to + * perform pagination. The default implementation is to call + * the controller's createUrl method with the page information. + * You may override this method if your URL scheme is not the same as + * the one supported by the controller's createUrl method. + * @param CController the controller that will create the actual URL + * @param integer the page that the URL should point to. This is a zero-based index. + * @return string the created URL + */ + public function createPageUrl($controller,$page) + { + $params=($this->route==='')?$_GET:array(); + if($page>0) // page 0 is the default + $params[$this->pageVar]=$page+1; + else + unset($params[$this->pageVar]); + return $controller->createUrl($this->route,$params); + } + + /** + * Applies LIMIT and OFFSET to the specified query criteria. + * @param CDbCriteria the query criteria that should be applied with the limit + * @since 1.0.1 + */ + public function applyLimit($criteria) + { + $criteria->limit=$this->pageSize; + $criteria->offset=$this->currentPage*$this->pageSize; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/CSort.php b/web/framework-1.0.8/web/CSort.php new file mode 100644 index 0000000..7bde5ea --- /dev/null +++ b/web/framework-1.0.8/web/CSort.php @@ -0,0 +1,292 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CSort represents information relevant to sorting. + * + * When data needs to be sorted according to one or several attributes, + * we can use CSort to represent the sorting information and generate + * appropriate hyperlinks that can lead to sort actions. + * + * CSort is designed to be used together with {@link CActiveRecord}. + * When creating a CSort instance, you need to specify {@link modelClass}. + * You can use CSort to generate hyperlinks by calling {@link link}. + * You can also use CSort to modify a {@link CDbCriteria} instance so that + * it can cause the query results to be sorted according to the specified + * attributes. + * + * CSort is primarily used for active record involving a single DB table. + * In order to use it with relational active record, special care needs to be taken. + * We use an example to illustrate this use case. Assume 'Post' is the main + * active record class, and 'author' is one of its related attributes. We would + * like to sort by post's title as well as its author's name. First, we need + * to define aliases for the two attributes by setting the {@link attributes} property: + *
    + * array(
    + *     'title',
    + *     'author.name'=>'authorName',
    + * )
    + * 
    + * + * We also need to modify the 'author' relation in 'Post' class and explicitly + * specify the 'alias' option with value 'author': + *
    + * 'author'=>array(self::BELONGS_TO, 'User', 'alias'=>'author')
    + * 
    + * + * Finally, we can use the following code to generate hyperlinks: + *
    + * echo CSort::link('title');
    + * echo CSort::link('author.name');
    + * 
    + * + * @author Qiang Xue + * @version $Id: CSort.php 699 2009-02-17 21:25:52Z qiang.xue $ + * @package system.web + * @since 1.0.1 + */ +class CSort extends CComponent +{ + /** + * @var boolean whether the sorting can be applied to multiple attributes simultaneously. + * Defaults to true. If false, each time the data can only be sorted by one attribute. + */ + public $multiSort=true; + /** + * @var string the class name of data models that need to be sorted. + * This should be a child class of {@link CActiveRecord}. + */ + public $modelClass; + /** + * @var array list of attributes that are allowed to be sorted. + * For example, array('user_id','create_time') would specify that only 'user_id' + * and 'create_time' can be sorted. + * Defaults to null, meaning all attributes of the {@link modelClass} can be sorted. + * This property can also be used to specify attribute aliases that should appear + * in the 'sort' GET parameter in place of the original attribute names. + * In this case, the aliases should be array values while the attribute names + * should be the corresponding array keys. Do not use '-' and '.' in the aliases + * as they are used as {@link separators}. + */ + public $attributes; + /** + * @var string the name of the GET parameter that specifies which attributes to be sorted + * in which direction. Defaults to 'sort'. + */ + public $sortVar='sort'; + /** + * @var string the default order that should be applied to the query criteria when + * the current request does not specify any sort. + */ + public $defaultOrder; + /** + * @var string the route (controller ID and action ID) for generating the sorted contents. + * Defaults to empty string, meaning using the currently requested route. + */ + public $route=''; + /** + * @var array separators used in the generated URL. This must be an array consisting of + * two elements. The first element specifies the character separating different + * attributes, while the second element specifies the character separating attribute name + * and the corresponding sort direction. Defaults to array('-','.'). + */ + public $separators=array('-','.'); + + private $_directions; + + /** + * Constructor. + * @param string the class name of data models that need to be sorted. + * This should be a child class of {@link CActiveRecord}. + */ + public function __construct($modelClass) + { + $this->modelClass=$modelClass; + } + + /** + * Modifies the query criteria by changing its ORDER BY property. + * @param CDbCriteria the query criteria + */ + public function applyOrder($criteria) + { + $directions=$this->getDirections(); + if(empty($directions)) + $order=$this->defaultOrder; + else + { + $schema=CActiveRecord::model($this->modelClass)->getDbConnection()->getSchema(); + $orders=array(); + foreach($directions as $attribute=>$descending) + { + if(($pos=strpos($attribute,'.'))!==false) + $attribute=$schema->quoteTableName(substr($attribute,0,$pos)).'.'.$schema->quoteColumnName(substr($attribute,$pos+1)); + else + $attribute=$schema->quoteColumnName($attribute); + $orders[]=$descending?$attribute.' DESC':$attribute; + } + $order=implode(', ',$orders); + } + + if(!empty($order)) + { + if(!empty($criteria->order)) + $criteria->order.=', '; + $criteria->order.=$order; + } + } + + /** + * Generates a hyperlink that can be clicked to cause sorting. + * @param string the attribute name. This must be the actual attribute name, not alias. + * If it is an attribute of a related AR object, the name should be prefixed with + * the relation name (e.g. 'author.name', where 'author' is the relation name). + * @param string the link label. If null, the label will be determined according + * to the attribute (see {@link CActiveRecord::getAttributeLabel}). + * @param array additional HTML attributes for the hyperlink tag + * @return string the generated hyperlink + */ + public function link($attribute,$label=null,$htmlOptions=array()) + { + $directions=$this->getDirections(); + if(isset($directions[$attribute])) + { + $descending=!$directions[$attribute]; + unset($directions[$attribute]); + } + else + $descending=false; + if($this->multiSort) + $directions=array_merge(array($attribute=>$descending),$directions); + else + $directions=array($attribute=>$descending); + + if($label===null) + $label=$this->resolveLabel($attribute); + $url=$this->createUrl(Yii::app()->getController(),$directions); + + return $this->createLink($attribute,$label,$url,$htmlOptions); + } + + /** + * Resolves the attribute label based on label definition in the AR class. + * @param string the attribute name. + * @return string the attribute label + * @since 1.0.2 + */ + protected function resolveLabel($attribute) + { + if(($pos=strpos($attribute,'.'))!==false) + { + $baseModel=CActiveRecord::model($this->modelClass); + if(($relation=$baseModel->getActiveRelation(substr($attribute,0,$pos)))!==null) + return CActiveRecord::model($relation->className)->getAttributeLabel(substr($attribute,$pos+1)); + else + return $baseModel->getAttributeLabel(substr($attribute,$pos+1)); + } + return CActiveRecord::model($this->modelClass)->getAttributeLabel($attribute); + } + + /** + * Returns the currently requested sort information. + * @return array sort directions indexed by attribute names. + * The sort direction is true if the corresponding attribute should be + * sorted in descending order. + */ + public function getDirections() + { + if($this->_directions===null) + { + $this->_directions=array(); + if(isset($_GET[$this->sortVar])) + { + $attributes=explode($this->separators[0],$_GET[$this->sortVar]); + foreach($attributes as $attribute) + { + if(($pos=strpos($attribute,$this->separators[1]))!==false) + { + $descending=substr($attribute,$pos+1)==='desc'; + $attribute=substr($attribute,0,$pos); + } + else + $descending=false; + + if(($attribute=$this->validateAttribute($attribute))!==false) + $this->_directions[$attribute]=$descending; + } + if(!$this->multiSort) + { + foreach($this->_directions as $attribute=>$descending) + return $this->_directions=array($attribute=>$descending); + } + } + } + return $this->_directions; + } + + /** + * Creates a URL that can lead to generating sorted data. + * @param CController the controller that will be used to create the URL. + * @param array the sort directions indexed by attribute names. + * The sort direction is true if the corresponding attribute should be + * sorted in descending order. + * @return string the URL for sorting + */ + public function createUrl($controller,$directions) + { + $sorts=array(); + foreach($directions as $attribute=>$descending) + { + if(is_array($this->attributes) && isset($this->attributes[$attribute])) + $attribute=$this->attributes[$attribute]; + $sorts[]=$descending ? $attribute.$this->separators[1].'desc' : $attribute; + } + $params=$_GET; + $params[$this->sortVar]=implode($this->separators[0],$sorts); + return $controller->createUrl($this->route,$params); + } + + /** + * Validates an attribute that is requested to be sorted. + * The validation is based on {@link attributes} and {@link CActiveRecord::attributeNames}. + * False will be returned if the attribute is not allowed to be sorted. + * If the attribute is aliased via {@link attributes}, the original + * attribute name will be returned. + * @param string the attribute name (could be an alias) that the user requests to sort on + * @return string the real attribute name. False if the attribute cannot be sorted + */ + protected function validateAttribute($attribute) + { + if(empty($this->attributes)) + $attributes=CActiveRecord::model($this->modelClass)->attributeNames(); + else + $attributes=$this->attributes; + foreach($attributes as $name=>$alias) + { + if($alias===$attribute) + return is_string($name) ? $name : $alias; + } + return false; + } + + /** + * Creates a hyperlink based on the given label and URL. + * You may override this method to customize the link generation. + * @param string the name of the attribute that this link is for + * @param string the label of the hyperlink + * @param string the URL + * @param array additional HTML options + * @return string the generated hyperlink + */ + protected function createLink($attribute,$label,$url,$htmlOptions) + { + return CHtml::link($label,$url,$htmlOptions); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/CTheme.php b/web/framework-1.0.8/web/CTheme.php new file mode 100644 index 0000000..422901e --- /dev/null +++ b/web/framework-1.0.8/web/CTheme.php @@ -0,0 +1,124 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTheme represents an application theme. + * + * @author Qiang Xue + * @version $Id: CTheme.php 837 2009-03-14 11:51:40Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CTheme extends CComponent +{ + private $_name; + private $_basePath; + private $_baseUrl; + + /** + * Constructor. + * @param string name of the theme + * @param string base theme path + * @param string base theme URL + */ + public function __construct($name,$basePath,$baseUrl) + { + $this->_name=$name; + $this->_baseUrl=$baseUrl; + $this->_basePath=$basePath; + } + + /** + * @return string theme name + */ + public function getName() + { + return $this->_name; + } + + /** + * @return string the relative URL to the theme folder (without ending slash) + */ + public function getBaseUrl() + { + return $this->_baseUrl; + } + + /** + * @return string the file path to the theme folder + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * @return string the path for controller views. Defaults to 'ThemeRoot/views'. + */ + public function getViewPath() + { + return $this->_basePath.DIRECTORY_SEPARATOR.'views'; + } + + /** + * @return string the path for system views. Defaults to 'ThemeRoot/views/system'. + */ + public function getSystemViewPath() + { + return $this->getViewPath().DIRECTORY_SEPARATOR.'system'; + } + + /** + * Finds the view file for the specified controller's view. + * @param CController the controller + * @param string the view name + * @return string the view file path. False if the file does not exist. + */ + public function getViewFile($controller,$viewName) + { + return $controller->resolveViewFile($viewName,$this->getViewPath().'/'.$controller->getUniqueId(),$this->getViewPath()); + } + + /** + * Finds the layout file for the specified controller's layout. + * @param CController the controller + * @param string the layout name + * @return string the layout file path. False if the file does not exist. + */ + public function getLayoutFile($controller,$layoutName) + { + $basePath=$this->getViewPath(); + if(empty($layoutName)) + { + $module=$controller->getModule(); + while($module!==null) + { + if($module->layout===false) + return false; + if(!empty($module->layout)) + break; + $module=$module->getParentModule(); + } + if($module===null) + return $controller->resolveViewFile(Yii::app()->layout,$basePath.'/layouts',$basePath); + else + { + $basePath.='/'.$module->getId(); + return $controller->resolveViewFile($module->layout,$basePath.'/layouts',$basePath); + } + } + else + { + if(($module=$controller->getModule())!==null) + $basePath.='/'.$module->getId(); + return $controller->resolveViewFile($layoutName,$basePath.'/layouts',$basePath); + } + } +} diff --git a/web/framework-1.0.8/web/CThemeManager.php b/web/framework-1.0.8/web/CThemeManager.php new file mode 100644 index 0000000..2cb5e5c --- /dev/null +++ b/web/framework-1.0.8/web/CThemeManager.php @@ -0,0 +1,127 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CThemeManager manages the themes for the Web application. + * + * A theme is a collection of view/layout files and resource files + * (e.g. css, image, js files). When a theme is active, {@link CController} + * will look for the specified view/layout under the theme folder first. + * The corresponding view/layout files will be used if the theme provides them. + * Otherwise, the default view/layout files will be used. + * + * By default, each theme is organized as a directory whose name is the theme name. + * All themes are located under the "WebRootPath/themes" directory. + * + * To activate a theme, set the {@link CWebApplication::setTheme theme} property + * to be the name of that theme. + * + * Since a self-contained theme often contains resource files that are made + * Web accessible, please make sure the view/layout files are protected from Web access. + * + * @author Qiang Xue + * @version $Id: CThemeManager.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CThemeManager extends CApplicationComponent +{ + /** + * default themes base path + */ + const DEFAULT_BASEPATH='themes'; + + /** + * @var string the name of the theme class for representing a theme. + * Defaults to {@link CTheme}. This can also be a class name in dot syntax. + */ + public $themeClass='CTheme'; + + private $_basePath=null; + private $_baseUrl=null; + + + /** + * @param string name of the theme to be retrieved + * @return CTheme the theme retrieved. Null if the theme does not exist. + */ + public function getTheme($name) + { + $themePath=$this->getBasePath().DIRECTORY_SEPARATOR.$name; + if(is_dir($themePath)) + { + $class=Yii::import($this->themeClass); + return new $class($name,$themePath,$this->getBaseUrl().'/'.$name); + } + else + return null; + } + + /** + * @return array list of available theme names + */ + public function getThemeNames() + { + static $themes; + if($themes===null) + { + $themes=array(); + $basePath=$this->getBasePath(); + $folder=@opendir($basePath); + while($file=@readdir($folder)) + { + if($file!=='.' && $file!=='..' && $file!=='.svn' && is_dir($basePath.DIRECTORY_SEPARATOR.$file)) + $themes[]=$file; + } + closedir($folder); + sort($themes); + } + return $themes; + } + + /** + * @return string the base path for all themes. Defaults to "WebRootPath/themes". + */ + public function getBasePath() + { + if($this->_basePath===null) + $this->setBasePath(dirname(Yii::app()->getRequest()->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH); + return $this->_basePath; + } + + /** + * @param string the base path for all themes. + * @throws CException if the base path does not exist + */ + public function setBasePath($value) + { + $this->_basePath=realpath($value); + if($this->_basePath===false || !is_dir($this->_basePath)) + throw new CException(Yii::t('yii','Theme directory "{directory}" does not exist.',array('{directory}'=>$value))); + } + + /** + * @return string the base URL for all themes. Defaults to "/WebRoot/themes". + */ + public function getBaseUrl() + { + if($this->_baseUrl===null) + $this->_baseUrl=Yii::app()->getBaseUrl().'/'.self::DEFAULT_BASEPATH; + return $this->_baseUrl; + } + + /** + * @param string the base URL for all themes. + */ + public function setBaseUrl($value) + { + $this->_baseUrl=rtrim($value,'/'); + } +} diff --git a/web/framework-1.0.8/web/CUploadedFile.php b/web/framework-1.0.8/web/CUploadedFile.php new file mode 100644 index 0000000..6c28f33 --- /dev/null +++ b/web/framework-1.0.8/web/CUploadedFile.php @@ -0,0 +1,210 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUploadedFile represents the information for an uploaded file. + * + * Call {@link getInstance} to retrieve the instance of an uploaded file, + * and then use {@link saveAs} to save it on the server. + * You may also query other information about the file, including {@link name}, + * {@link tempName}, {@link type}, {@link size} and {@link error}. + * + * @author Qiang Xue + * @version $Id: CUploadedFile.php 1191 2009-06-29 18:10:06Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CUploadedFile extends CComponent +{ + private $_name; + private $_tempName; + private $_type; + private $_size; + private $_error; + + /** + * Returns an instance of the specified uploaded file. + * The file should be uploaded using {@link CHtml::activeFileField}. + * @param CModel the model instance + * @param string the attribute name. For tabular file uploading, this can be in the format of "attributeName[$i]", where $i stands for an integer index. + * @return CUploadedFile the instance of the uploaded file. + * Null is returned if no file is uploaded for the specified model attribute. + * @see getInstanceByName + */ + public static function getInstance($model,$attribute) + { + if(($pos=strpos($attribute,'['))!==false) + $name=get_class($model).substr($attribute,$pos).'['.substr($attribute,0,$pos).']'; + else + $name=get_class($model).'['.$attribute.']'; + return self::getInstanceByName($name); + } + + /** + * Returns an instance of the specified uploaded file. + * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]'). + * @param string the name of the file input field. + * @return CUploadedFile the instance of the uploaded file. + * Null is returned if no file is uploaded for the specified name. + */ + public static function getInstanceByName($name) + { + static $files; + if($files===null) + { + $files=array(); + if(isset($_FILES) && is_array($_FILES)) + { + foreach($_FILES as $class=>$info) + { + if(is_array($info['name'])) + { + $keys=array_keys($info['name']); + foreach($keys as $key) + { + if(is_array($info['name'][$key])) + { + $subKeys=array_keys($info['name'][$key]); + foreach($subKeys as $subKey) + $files["{$class}[{$key}][{$subKey}]"]=new CUploadedFile($info['name'][$key][$subKey],$info['tmp_name'][$key][$subKey],$info['type'][$key][$subKey],$info['size'][$key][$subKey],$info['error'][$key][$subKey]); + } + else + $files["{$class}[{$key}]"]=new CUploadedFile($info['name'][$key],$info['tmp_name'][$key],$info['type'][$key],$info['size'][$key],$info['error'][$key]); + } + } + else + $files[$class]=new CUploadedFile($info['name'],$info['tmp_name'],$info['type'],$info['size'],$info['error']); + } + } + } + + return isset($files[$name]) && $files[$name]->getError()!=UPLOAD_ERR_NO_FILE ? $files[$name] : null; + } + + /** + * Constructor. + * Use {@link getInstance} to get an instance of an uploaded file. + * @param string the original name of the file being uploaded + * @param string the path of the uploaded file on the server. + * @param string the MIME-type of the uploaded file (such as "image/gif"). + * @param integer the actual size of the uploaded file in bytes + * @param integer the error code + */ + protected function __construct($name,$tempName,$type,$size,$error) + { + $this->_name=$name; + $this->_tempName=$tempName; + $this->_type=$type; + $this->_size=$size; + $this->_error=$error; + } + + /** + * String output. + * This is PHP magic method that returns string representation of an object. + * The implementation here returns the uploaded file's name. + * @return string the string representation of the object + * @since 1.0.2 + */ + public function __toString() + { + return $this->_name; + } + + /** + * Saves the uploaded file. + * @param string the file path used to save the uploaded file + * @param boolean whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again in the current request. + * @return boolean true whether the file is saved successfully + */ + public function saveAs($file,$deleteTempFile=true) + { + if($this->_error===UPLOAD_ERR_OK) + { + if($deleteTempFile) + return move_uploaded_file($this->_tempName,$file); + else if(is_uploaded_file($this->_tempName)) + return file_put_contents($file,file_get_contents($this->_tempName))!==false; + else + return false; + } + else + return false; + } + + /** + * @return string the original name of the file being uploaded + */ + public function getName() + { + return $this->_name; + } + + /** + * @return string the path of the uploaded file on the server. + * Note, this is a temporary file which will be automatically deleted by PHP + * after the current request is processed. + */ + public function getTempName() + { + return $this->_tempName; + } + + /** + * @return string the MIME-type of the uploaded file (such as "image/gif"). + * Since this MIME type is not checked on the server side, do not take this value for granted. + * Instead, use {@link CFileHelper::getMimeType} to determine the exact MIME type. + */ + public function getType() + { + return $this->_type; + } + + /** + * @return integer the actual size of the uploaded file in bytes + */ + public function getSize() + { + return $this->_size; + } + + /** + * Returns an error code describing the status of this file uploading. + * @return integer the error code + * @see http://www.php.net/manual/en/features.file-upload.errors.php + */ + public function getError() + { + return $this->_error; + } + + /** + * @return boolean whether there is an error with the uploaded file. + * Check {@link error} for detailed error code information. + */ + public function getHasError() + { + return $this->_error!=UPLOAD_ERR_OK; + } + + /** + * @return string the file extension name for {@link name}. + * The extension name does not include the dot character. An empty string + * is returned if {@link name} does not have an extension name. + */ + public function getExtensionName() + { + if(($pos=strrpos($this->_name,'.'))!==false) + return (string)substr($this->_name,$pos+1); + else + return ''; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/CUrlManager.php b/web/framework-1.0.8/web/CUrlManager.php new file mode 100644 index 0000000..17fe18a --- /dev/null +++ b/web/framework-1.0.8/web/CUrlManager.php @@ -0,0 +1,613 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUrlManager manages the URLs of Yii Web applications. + * + * It provides URL construction ({@link createUrl()}) as well as parsing ({@link parseUrl()}) functionality. + * + * URLs managed via CUrlManager can be in one of the following two formats, + * by setting {@link setUrlFormat urlFormat} property: + *
      + *
    • 'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...
    • + *
    • 'get' format: /path/to/EntryScript.php?name1=value1&name2=value2...
    • + *
    + * + * When using 'path' format, CUrlManager uses a set of {@link setRules rules} to: + *
      + *
    • parse the requested URL into a route ('ControllerID/ActionID') and GET parameters;
    • + *
    • create URLs based on the given route and GET parameters.
    • + *
    + * + * A rule consists of a route and a pattern. The latter is used by CUrlManager to determine + * which rule is used for parsing/creating URLs. A pattern is meant to match the path info + * part of a URL. It may contain named parameters using the syntax '<ParamName:RegExp>'. + * + * When parsing a URL, a matching rule will extract the named parameters from the path info + * and put them into the $_GET variable; when creating a URL, a matching rule will extract + * the named parameters from $_GET and put them into the path info part of the created URL. + * + * If a pattern ends with '/*', it means additional GET parameters may be appended to the path + * info part of the URL; otherwise, the GET parameters can only appear in the query string part. + * + * To specify URL rules, set the {@link setRules rules} property as an array of rules (pattern=>route). + * For example, + *
    + * array(
    + *     'articles'=>'article/list',
    + *     'article//*'=>'article/read',
    + * )
    + * 
    + * Two rules are specified in the above: + *
      + *
    • The first rule says that if the user requests the URL '/path/to/index.php/articles', + * it should be treated as '/path/to/index.php/article/list'; and vice versa applies + * when constructing such a URL.
    • + *
    • The second rule contains a named parameter 'id' which is specified using + * the <ParamName:RegExp> syntax. It says that if the user requests the URL + * '/path/to/index.php/article/13', it should be treated as '/path/to/index.php/article/read?id=13'; + * and vice versa applies when constructing such a URL.
    • + *
    + * + * Starting from version 1.0.5, the route part may contain references to named parameters defined + * in the pattern part. This allows a rule to be applied to different routes based on matching criteria. + * For example, + *
    + * array(
    + *      '<_c:(post|comment)>//<_a:(create|update|delete)>'=>'<_c>/<_a>',
    + *      '<_c:(post|comment)>/'=>'<_a>/view',
    + *      '<_c:(post|comment)>s/*'=>'<_a>/list',
    + * )
    + * 
    + * In the above, we use two named parameters '<_c>' and '<_a>' in the route part. The '<_c>' + * parameter matches either 'post' or 'comment', while the '<_a>' parameter matches an action ID. + * + * Like normal rules, these rules can be used for both parsing and creating URLs. + * For example, using the rules above, the URL '/index.php/post/123/create' + * would be parsed as the route 'post/create' with GET parameter 'id' being 123. + * And given the route 'post/list' and GET parameter 'page' being 2, we should get a URL + * '/index.php/posts/page/2'. + * + * CUrlManager is a default application component that may be accessed via + * {@link CWebApplication::getUrlManager()}. + * + * @author Qiang Xue + * @version $Id: CUrlManager.php 1292 2009-08-06 18:43:07Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CUrlManager extends CApplicationComponent +{ + const CACHE_KEY='Yii.CUrlManager.rules'; + const GET_FORMAT='get'; + const PATH_FORMAT='path'; + + /** + * @var array the URL rules (pattern=>route). + */ + public $rules=array(); + /** + * @var string the URL suffix used when in 'path' format. + * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty. + */ + public $urlSuffix=''; + /** + * @var boolean whether to show entry script name in the constructed URL. Defaults to true. + */ + public $showScriptName=true; + /** + * @var boolean whether to append GET parameters to the path info part. Defaults to true. + * This property is only effective when {@link urlFormat} is 'path' and is mainly used when + * creating URLs. When it is true, GET parameters will be appended to the path info and + * separate from each other using slashes. If this is false, GET parameters will be in query part. + * @since 1.0.3 + */ + public $appendParams=true; + /** + * @var string the GET variable name for route. Defaults to 'r'. + */ + public $routeVar='r'; + /** + * @var boolean whether routes are case-sensitive. Defaults to true. By setting this to false, + * the route in the incoming request will be turned to lower case first before further processing. + * As a result, you should follow the convention that you use lower case when specifying + * controller mapping ({@link CWebApplication::controllerMap}) and action mapping + * ({@link CController::actions}). Also, the directory names for organizing controllers should + * be in lower case. + * @since 1.0.1 + */ + public $caseSensitive=true; + /** + * @var string the ID of the cache application component that is used to cache the parsed URL rules. + * Defaults to 'cache' which refers to the primary cache application component. + * Set this property to false if you want to disable caching URL rules. + * @since 1.0.3 + */ + public $cacheID='cache'; + /** + * @var boolean whether to enable strict URL parsing. + * This property is only effective when {@link urlFormat} is 'path'. + * If it is set true, then an incoming URL must match one of the {@link rules URL rules}. + * Otherwise, it will be treated as an invalid request and trigger a 404 HTTP exception. + * Defaults to false. + * @since 1.0.6 + */ + public $useStrictParsing=false; + + private $_urlFormat=self::GET_FORMAT; + private $_rules=array(); + private $_baseUrl; + + + /** + * Initializes the application component. + */ + public function init() + { + parent::init(); + $this->processRules(); + } + + /** + * Processes the URL rules. + */ + protected function processRules() + { + if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT) + return; + if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null) + { + $hash=md5(serialize($this->rules)); + if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash) + { + $this->_rules=$data[0]; + return; + } + } + foreach($this->rules as $pattern=>$route) + $this->_rules[]=new CUrlRule($route,$pattern); + if(isset($cache)) + $cache->set(self::CACHE_KEY,array($this->_rules,$hash)); + } + + /** + * Constructs a URL. + * @param string the controller and the action (e.g. article/read) + * @param array list of GET parameters (name=>value). Both the name and value will be URL-encoded. + * If the name is '#', the corresponding value will be treated as an anchor + * and will be appended at the end of the URL. This anchor feature has been available since version 1.0.1. + * @param string the token separating name-value pairs in the URL. Defaults to '&'. + * @return string the constructed URL + */ + public function createUrl($route,$params=array(),$ampersand='&') + { + unset($params[$this->routeVar]); + foreach($params as &$param) + if($param===null) + $param=''; + if(isset($params['#'])) + { + $anchor='#'.$params['#']; + unset($params['#']); + } + else + $anchor=''; + $route=trim($route,'/'); + foreach($this->_rules as $rule) + { + if(($url=$rule->createUrl($this,$route,$params,$ampersand))!==false) + return $this->getBaseUrl().'/'.$url.$anchor; + } + + $url = $this->createUrlDefault($route,$params,$ampersand).$anchor; + return $url; + } + + /** + * Contructs a URL based on default settings. + * @param string the controller and the action (e.g. article/read) + * @param array list of GET parameters + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + protected function createUrlDefault($route,$params,$ampersand) + { + if($this->getUrlFormat()===self::PATH_FORMAT) + { + $url=rtrim($this->getBaseUrl().'/'.$route,'/'); + if($this->appendParams) + { + $url=rtrim($url.'/'.$this->createPathInfo($params,'/','/'),'/'); + return $route==='' ? $url : $url.$this->urlSuffix; + } + else + { + if($route!=='') + $url.=$this->urlSuffix; + $query=$this->createPathInfo($params,'=',$ampersand); + return $query==='' ? $url : $url.'?'.$query; + } + } + else + { + $url=$this->getBaseUrl(); + if(!$this->showScriptName) + $url.='/'; + if($route!=='') + { + $url.='?'.$this->routeVar.'='.$route; + if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') + $url.=$ampersand.$query; + } + else if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') + $url.='?'.$query; + return $url; + } + } + + /** + * Parses the user request. + * @param CHttpRequest the request application component + * @return string the route (controllerID/actionID) and perhaps GET parameters in path format. + */ + public function parseUrl($request) + { + if($this->getUrlFormat()===self::PATH_FORMAT && 0) + { + $rawPathInfo=urldecode($request->getPathInfo()); + $pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix); + foreach($this->_rules as $rule) + { + if(($r=$rule->parseUrl($this,$pathInfo,$rawPathInfo))!==false) + return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r; + } + if($this->useStrictParsing) + throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', + array('{route}'=>$pathInfo))); + else + return $pathInfo; + } + else if(isset($_GET[$this->routeVar])) + return $_GET[$this->routeVar]; + else if(isset($_POST[$this->routeVar])) + return $_POST[$this->routeVar]; + else + return ''; + } + + /** + * Parses a path info into URL segments and saves them to $_GET. + * @param string path info + * @since 1.0.3 + */ + public static function parsePathInfo($pathInfo) + { + if($pathInfo==='') + return; + $segs=explode('/',$pathInfo.'/'); + $n=count($segs); + for($i=0;$i<$n-1;$i+=2) + { + $key=$segs[$i]; + if($key==='') continue; + $value=$segs[$i+1]; + if(($pos=strpos($key,'[]'))!==false) + $_GET[substr($key,0,$pos)][]=$value; + else + $_GET[$key]=$value; + } + } + + /** + * Creates a path info based on the given parameters. + * @param array list of GET parameters + * @param string the separator between name and value + * @param string the separator between name-value pairs + * @return string the created path info + * @since 1.0.3 + */ + public function createPathInfo($params,$equal,$ampersand) + { + $pairs=array(); + foreach($params as $key=>$value) + { + if(is_array($value)) + { + foreach($value as $k=>$v) + $pairs[]=urlencode($key).'['.urlencode($k).']'.$equal.urlencode($v); + } + else + $pairs[]=urlencode($key).$equal.urlencode($value); + } + return implode($ampersand,$pairs); + } + + /** + * Removes the URL suffix from path info. + * @param string path info part in the URL + * @param string the URL suffix to be removed + * @return string path info with URL suffix removed. + */ + public function removeUrlSuffix($pathInfo,$urlSuffix) + { + if($urlSuffix!=='' && substr($pathInfo,-strlen($urlSuffix))===$urlSuffix) + return substr($pathInfo,0,-strlen($urlSuffix)); + else + return $pathInfo; + } + + /** + * @return string the base URL of the application (the part after host name and before query string). + * If {@link showScriptName} is true, it will include the script name part. + * Otherwise, it will not, and the ending slashes are stripped off. + */ + public function getBaseUrl() + { + if($this->_baseUrl!==null) + return $this->_baseUrl; + else + { + if($this->showScriptName) + $this->_baseUrl=Yii::app()->getRequest()->getScriptUrl(); + else + $this->_baseUrl=Yii::app()->getRequest()->getBaseUrl(); + return $this->_baseUrl; + } + } + + /** + * @return string the URL format. Defaults to 'path'. + */ + public function getUrlFormat() + { + return $this->_urlFormat; + } + + /** + * @param string the URL format. It must be either 'path' or 'get'. + */ + public function setUrlFormat($value) + { + if($value===self::PATH_FORMAT || $value===self::GET_FORMAT) + $this->_urlFormat=$value; + else + throw new CException(Yii::t('yii','CUrlManager.UrlFormat must be either "path" or "get".')); + } +} + + +/** + * CUrlRule represents a URL formatting/parsing rule. + * + * It mainly consists of two parts: route and pattern. The former classifies + * the rule so that it only applies to specific controller-action route. + * The latter performs the actual formatting and parsing role. The pattern + * may have a set of named parameters. + * + * @author Qiang Xue + * @version $Id: CUrlManager.php 1292 2009-08-06 18:43:07Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CUrlRule extends CComponent +{ + /** + * @var string the URL suffix used for this rule. + * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. + * Defaults to null, meaning using the value of {@link CUrlManager::urlSuffix}. + * @since 1.0.6 + */ + public $urlSuffix; + /** + * @var boolean whether the rule is case sensitive. Defaults to null, meaning + * using the value of {@link CUrlManager::caseSensitive}. + * @since 1.0.1 + */ + public $caseSensitive; + /** + * @var array the default GET parameters (name=>value) that this rule provides. + * When this rule is used to parse the incoming request, the values declared in this property + * will be injected into $_GET. + * @since 1.0.8 + */ + public $defaultParams=array(); + /** + * @var string the controller/action pair + */ + public $route; + /** + * @var array the mapping from route param name to token name (e.g. _r1=><1>) + * @since 1.0.5 + */ + public $references=array(); + /** + * @var string the pattern used to match route + * @since 1.0.5 + */ + public $routePattern; + /** + * @var string regular expression used to parse a URL + */ + public $pattern; + /** + * @var string template used to construct a URL + */ + public $template; + /** + * @var array list of parameters (name=>regular expression) + */ + public $params=array(); + /** + * @var boolean whether the URL allows additional parameters at the end of the path info. + */ + public $append; + + /** + * Constructor. + * @param string the route of the URL (controller/action) + * @param string the pattern for matching the URL + */ + public function __construct($route,$pattern) + { + if(is_array($route)) + { + if(isset($route['urlSuffix'])) + $this->urlSuffix=$route['urlSuffix']; + if(isset($route['caseSensitive'])) + $this->caseSensitive=$route['caseSensitive']; + if(isset($route['defaultParams'])) + $this->defaultParams=$route['defaultParams']; + $route=$this->route=$route[0]; + } + else + $this->route=$route; + + $tr2['/']=$tr['/']='\\/'; + + if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2)) + { + foreach($matches2[1] as $name) + $this->references[$name]="<$name>"; + } + + if(preg_match_all('/<(\w+):?(.*?)?>/',$pattern,$matches)) + { + $tokens=array_combine($matches[1],$matches[2]); + foreach($tokens as $name=>$value) + { + $tr["<$name>"]="(?P<$name>".($value!==''?$value:'[^\/]+').')'; + if(isset($this->references[$name])) + $tr2["<$name>"]=$tr["<$name>"]; + else + $this->params[$name]=$value; + } + } + $p=rtrim($pattern,'*'); + $this->append=$p!==$pattern; + $p=trim($p,'/'); + $this->template=preg_replace('/<(\w+):?.*?>/','<$1>',$p); + $this->pattern='/^'.strtr($this->template,$tr).'\/'; + if($this->append) + $this->pattern.='/u'; + else + $this->pattern.='$/u'; + + if($this->references!==array()) + $this->routePattern='/^'.strtr($this->route,$tr2).'$/u'; + + if(YII_DEBUG && @preg_match($this->pattern,'test')===false) + throw new CException(Yii::t('yii','The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.', + array('{route}'=>$route,'{pattern}'=>$pattern))); + } + + /** + * Creates a URL based on this rule. + * @param CUrlManager the manager + * @param string the route + * @param array list of parameters + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + public function createUrl($manager,$route,$params,$ampersand) + { + if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) + $case=''; + else + $case='i'; + + $tr=array(); + if($route!==$this->route) + { + if($this->routePattern!==null && preg_match($this->routePattern.$case,$route,$matches)) + { + foreach($this->references as $key=>$name) + $tr[$name]=$matches[$key]; + } + else + return false; + } + + foreach($this->params as $key=>$value) + if(!isset($params[$key])) + return false; + + foreach($this->params as $key=>$value) + { + $tr["<$key>"]=urlencode($params[$key]); + unset($params[$key]); + } + + $suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; + + $url=strtr($this->template,$tr); + if(empty($params)) + return $url!=='' ? $url.$suffix : $url; + + if($this->append) + $url.='/'.$manager->createPathInfo($params,'/','/').$suffix; + else + { + if($url!=='') + $url.=$suffix; + $url.='?'.$manager->createPathInfo($params,'=',$ampersand); + } + return $url; + } + + /** + * Parases a URL based on this rule. + * @param CUrlManager the URL manager + * @param string path info part of the URL + * @param string path info that contains the potential URL suffix + * @return string the route that consists of the controller ID and action ID + */ + public function parseUrl($manager,$pathInfo,$rawPathInfo) + { + if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) + $case=''; + else + $case='i'; + + if($this->urlSuffix!==null) + $pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix); + + // URL suffix required, but not found in the requested URL + if($manager->useStrictParsing && $pathInfo===$rawPathInfo + && ($this->urlSuffix!='' || $this->urlSuffix===null && $manager->urlSuffix!='')) + throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', + array('{route}'=>$rawPathInfo))); + + $pathInfo.='/'; + if(preg_match($this->pattern.$case,$pathInfo,$matches)) + { + foreach($this->defaultParams as $name=>$value) + { + if(!isset($_GET[$name])) + $_GET[$name]=$value; + } + $tr=array(); + foreach($matches as $key=>$value) + { + if(isset($this->references[$key])) + $tr[$this->references[$key]]=$value; + else if(isset($this->params[$key])) + $_GET[$key]=$value; + } + if($pathInfo!==$matches[0]) // there're additional GET params + CUrlManager::parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/')); + if($this->routePattern!==null) + return strtr($this->route,$tr); + else + return $this->route; + } + else + return false; + } +} diff --git a/web/framework-1.0.8/web/CWebApplication.php b/web/framework-1.0.8/web/CWebApplication.php new file mode 100644 index 0000000..d5200ed --- /dev/null +++ b/web/framework-1.0.8/web/CWebApplication.php @@ -0,0 +1,608 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebApplication extends CApplication by providing functionalities specific to Web requests. + * + * CWebApplication manages the controllers in MVC pattern, and provides the following additional + * core application components: + *
      + *
    • {@link urlManager}: provides URL parsing and constructing functionality;
    • + *
    • {@link request}: encapsulates the Web request information;
    • + *
    • {@link session}: provides the session-related functionalities;
    • + *
    • {@link assetManager}: manages the publishing of private asset files.
    • + *
    • {@link user}: represents the user session information.
    • + *
    • {@link themeManager}: manages themes.
    • + *
    • {@link authManager}: manages role-based access control (RBAC).
    • + *
    • {@link clientScript}: manages client scripts (javascripts and CSS).
    • + *
    + * + * User requests are resolved as controller-action pairs and additional parameters. + * CWebApplication creates the requested controller instance and let it to handle + * the actual user request. If the user does not specify controller ID, it will + * assume {@link defaultController} is requested (which defaults to 'site'). + * + * Controller class files must reside under the directory {@link getControllerPath controllerPath} + * (defaults to 'protected/controllers'). The file name is the same as the controller + * name and the class name is the controller ID appended with 'Controller'. + * For example, the controller 'article' is defined by the class 'ArticleController' + * which is in the file 'protected/controller/article.php'. + * + * @author Qiang Xue + * @version $Id: CWebApplication.php 1053 2009-05-23 02:34:03Z qiang.xue $ + * @package system.web + * @since 1.0 + */ +class CWebApplication extends CApplication +{ + public $classFile; + + /** + * @return string the ID of the default controller. Defaults to 'site'. + */ + public $defaultController='site'; + /** + * @var mixed the application-wide layout. Defaults to 'main' (relative to {@link getLayoutPath layoutPath}). + * If this is false, then no layout will be used. + */ + public $layout='main'; + /** + * @var array mapping from controller ID to controller configurations. + * Each name-value pair specifies the configuration for a single controller. + * A controller configuration can be either a string or an array. + * If the former, the string should be the class name or + * {@link YiiBase::getPathOfAlias class path alias} of the controller. + * If the latter, the array must contain a 'class' element which specifies + * the controller's class name or {@link YiiBase::getPathOfAlias class path alias}. + * The rest name-value pairs in the array are used to initialize + * the corresponding controller properties. For example, + *
    +	 * array(
    +	 *   'post'=>array(
    +	 *      'class'=>'path.to.PostController',
    +	 *      'pageTitle'=>'something new',
    +	 *   ),
    +	 *   'user'=>'path.to.UserController',,
    +	 * )
    +	 * 
    + * + * Note, when processing an incoming request, the controller map will first be + * checked to see if the request can be handled by one of the controllers in the map. + * If not, a controller will be searched for under the {@link getControllerPath default controller path}. + */ + public $controllerMap=array(); + /** + * @var array the configuration specifying a controller which should handle + * all user requests. This is mainly used when the application is in maintenance mode + * and we should use a controller to handle all incoming requests. + * The configuration specifies the controller route (the first element) + * and GET parameters (the rest name-value pairs). For example, + *
    +	 * array(
    +	 *     'offline/notice',
    +	 *     'param1'=>'value1',
    +	 *     'param2'=>'value2',
    +	 * )
    +	 * 
    + * Defaults to null, meaning catch-all is not effective. + */ + public $catchAllRequest; + + private $_controllerPath; + private $_viewPath; + private $_systemViewPath; + private $_layoutPath; + private $_controller; + private $_homeUrl; + private $_theme; + + + /** + * Processes the current request. + * It first resolves the request into controller and action, + * and then creates the controller to perform the action. + */ + public function processRequest() + { + if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0])) + { + $route=$this->catchAllRequest[0]; + foreach(array_splice($this->catchAllRequest,1) as $name=>$value) + $_GET[$name]=$value; + } + else + $route=$this->getUrlManager()->parseUrl($this->getRequest()); + + $this->runController($route); + } + + /** + * Registers the core application components. + * This method overrides the parent implementation by registering additional core components. + * @see setComponents + */ + protected function registerCoreComponents() + { + parent::registerCoreComponents(); + + $components=array( + 'urlManager'=>array( + 'class'=>'CUrlManager', + ), + 'request'=>array( + 'class'=>'CHttpRequest', + ), + 'session'=>array( + 'class'=>'CHttpSession', + ), + 'assetManager'=>array( + 'class'=>'CAssetManager', + ), + 'user'=>array( + 'class'=>'CWebUser', + ), + 'themeManager'=>array( + 'class'=>'CThemeManager', + ), + 'authManager'=>array( + 'class'=>'CPhpAuthManager', + ), + 'clientScript'=>array( + 'class'=>'CClientScript', + ), + ); + + $this->setComponents($components); + } + + /** + * @return CHttpRequest the request component + */ + public function getRequest() + { + return $this->getComponent('request'); + } + + /** + * @return CUrlManager the URL manager component + */ + public function getUrlManager() + { + return $this->getComponent('urlManager'); + } + + /** + * @return IAuthManager the authorization manager component + */ + public function getAuthManager() + { + return $this->getComponent('authManager'); + } + + /** + * @return CAssetManager the asset manager component + */ + public function getAssetManager() + { + return $this->getComponent('assetManager'); + } + + /** + * @return CHttpSession the session component + */ + public function getSession() + { + return $this->getComponent('session'); + } + + /** + * @return CWebUser the user session information + */ + public function getUser() + { + return $this->getComponent('user'); + } + + /** + * Returns the view renderer. + * If this component is registered and enabled, the default + * view rendering logic defined in {@link CBaseController} will + * be replaced by this renderer. + * @return IViewRenderer the view renderer. + */ + public function getViewRenderer() + { + return $this->getComponent('viewRenderer'); + } + + /** + * Returns the client script manager. + * @return CClientScript the client script manager + */ + public function getClientScript() + { + return $this->getComponent('clientScript'); + } + + /** + * @return CThemeManager the theme manager. + */ + public function getThemeManager() + { + return $this->getComponent('themeManager'); + } + + /** + * @return CTheme the theme used currently. Null if no theme is being used. + */ + public function getTheme() + { + if(is_string($this->_theme)) + $this->_theme=$this->getThemeManager()->getTheme($this->_theme); + return $this->_theme; + } + + /** + * @param string the theme name + */ + public function setTheme($value) + { + $this->_theme=$value; + } + + /** + * Creates a relative URL based on the given controller and action information. + * @param string the URL route. This should be in the format of 'ControllerID/ActionID'. + * @param array additional GET parameters (name=>value). Both the name and value will be URL-encoded. + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + public function createUrl($route,$params=array(),$ampersand='&') + { + return $this->getUrlManager()->createUrl($route,$params,$ampersand); + } + + /** + * Creates an absolute URL based on the given controller and action information. + * @param string the URL route. This should be in the format of 'ControllerID/ActionID'. + * @param array additional GET parameters (name=>value). Both the name and value will be URL-encoded. + * @param string schema to use (e.g. http, https). If empty, the schema used for the current request will be used. + * @param string the token separating name-value pairs in the URL. + * @return string the constructed URL + */ + public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&') + { + return $this->getRequest()->getHostInfo($schema).$this->createUrl($route,$params,$ampersand); + } + + /** + * Returns the relative URL for the application. + * This is a shortcut method to {@link CHttpRequest::getBaseUrl()}. + * @param boolean whether to return an absolute URL. Defaults to false, meaning returning a relative one. + * This parameter has been available since 1.0.2. + * @return string the relative URL for the application + * @see CHttpRequest::getBaseUrl() + */ + public function getBaseUrl($absolute=false) + { + return $this->getRequest()->getBaseUrl($absolute); + } + + /** + * @return string the homepage URL + */ + public function getHomeUrl() + { + if($this->_homeUrl===null) + { + if($this->getUrlManager()->showScriptName) + return $this->getRequest()->getScriptUrl(); + else + return $this->getRequest()->getBaseUrl().'/'; + } + else + return $this->_homeUrl; + } + + /** + * @param string the homepage URL + */ + public function setHomeUrl($value) + { + $this->_homeUrl=$value; + } + + /** + * Creates the controller and performs the specified action. + * @param string the route of the current request. See {@link createController} for more details. + * @throws CHttpException if the controller could not be created. + */ + public function runController($route) + { + if(($ca=$this->createController($route))!==null) + { + list($controller,$actionID)=$ca; + $oldController=$this->_controller; + $this->_controller=$controller; + $controller->init(); + $controller->run($actionID); + $this->_controller=$oldController; + } + else + { + header('HTTP/1.1 404 Not found'); + exit; + } + // throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', + // array('{route}'=>$route===''?$this->defaultController:$route))); + } + + /** + * Creates a controller instance based on a route. + * The route should contain the controller ID and the action ID. + * It may also contain additional GET variables. All these must be concatenated together with slashes. + * + * This method will attempt to create a controller in the following order: + *
      + *
    1. If the first segment is found in {@link controllerMap}, the corresponding + * controller configuration will be used to create the controller;
    2. + *
    3. If the first segment is found to be a module ID, the corresponding module + * will be used to create the controller;
    4. + *
    5. Otherwise, it will search under the {@link controllerPath} to create + * the corresponding controller. For example, if the route is "admin/user/create", + * then the controller will be created using the class file "protected/controllers/admin/UserController.php".
    6. + *
    + * @param string the route of the request. + * @param CWebModule the module that the new controller will belong to. Defaults to null, meaning the application + * instance is the owner. + * @return array the controller instance and the action ID. Null if the controller class does not exist or the route is invalid. + */ + public function createController($route,$owner=null) + { + if($owner===null) + $owner=$this; + if(($route=trim($route,'/'))==='') + $route=$owner->defaultController; + $caseSensitive=$this->getUrlManager()->caseSensitive; + + $route.='/'; + while(($pos=strpos($route,'/'))!==false) + { + $id=substr($route,0,$pos); + if(!preg_match('/^\w+$/',$id)) + return null; + if(!$caseSensitive) + $id=strtolower($id); + $route=(string)substr($route,$pos+1); + if(!isset($basePath)) // first segment + { + if(isset($owner->controllerMap[$id])) + { + return array( + Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner), + $this->parseActionParams($route), + ); + } + + if(($module=$owner->getModule($id))!==null) + return $this->createController($route,$module); + + $basePath=$owner->getControllerPath(); + $controllerID=''; + } + else + $controllerID.='/'; + + $className=ucfirst($id).'Controller'; + // $this->classFile=$basePath.DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.$className.'.php'; + $this->classFile = GetSSModulePath($className); + if(is_file($this->classFile)) + { + if(!class_exists($className,false)) + require($this->classFile); + + if(class_exists($className,false) && is_subclass_of($className,'CController')) + { + $id[0]=strtolower($id[0]); + return array( + new $className($controllerID.$id,$owner===$this?null:$owner), + $this->parseActionParams($route), + ); + } + return null; + } + $controllerID.=$id; + $basePath.=DIRECTORY_SEPARATOR.$id; + } + } + + /** + * Parses a path info into an action ID and GET variables. + * @param string path info + * @return string action ID + * @since 1.0.3 + */ + protected function parseActionParams($pathInfo) + { + if(($pos=strpos($pathInfo,'/'))!==false) + { + CUrlManager::parsePathInfo((string)substr($pathInfo,$pos+1)); + $actionID=substr($pathInfo,0,$pos); + return $this->getUrlManager()->caseSensitive ? $actionID : strtolower($actionID); + } + else + return $pathInfo; + } + + /** + * @return CController the currently active controller + */ + public function getController() + { + return $this->_controller; + } + + /** + * @param CController the currently active controller + * @since 1.0.6 + */ + public function setController($value) + { + $this->_controller=$value; + } + + /** + * @return string the directory that contains the controller classes. Defaults to 'protected/controllers'. + */ + public function getControllerPath() + { + if($this->_controllerPath!==null) + return $this->_controllerPath; + else + return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'controllers'; + } + + /** + * @param string the directory that contains the controller classes. + * @throws CException if the directory is invalid + */ + public function setControllerPath($value) + { + if(($this->_controllerPath=realpath($value))===false || !is_dir($this->_controllerPath)) + throw new CException(Yii::t('yii','The controller path "{path}" is not a valid directory.', + array('{path}'=>$value))); + } + + /** + * @return string the root directory of view files. Defaults to 'protected/views'. + */ + public function getViewPath() + { + if($this->_viewPath!==null) + return $this->_viewPath; + else + return $this->_viewPath=$this->getBasePath().DIRECTORY_SEPARATOR.'views'; + } + + /** + * @param string the root directory of view files. + * @throws CException if the directory does not exist. + */ + public function setViewPath($path) + { + if(($this->_viewPath=realpath($path))===false || !is_dir($this->_viewPath)) + throw new CException(Yii::t('yii','The view path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * @return string the root directory of system view files. Defaults to 'protected/views/system'. + */ + public function getSystemViewPath() + { + if($this->_systemViewPath!==null) + return $this->_systemViewPath; + else + return $this->_systemViewPath=$this->getViewPath().DIRECTORY_SEPARATOR.'system'; + } + + /** + * @param string the root directory of system view files. + * @throws CException if the directory does not exist. + */ + public function setSystemViewPath($path) + { + if(($this->_systemViewPath=realpath($path))===false || !is_dir($this->_systemViewPath)) + throw new CException(Yii::t('yii','The system view path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * @return string the root directory of layout files. Defaults to 'protected/views/layouts'. + */ + public function getLayoutPath() + { + if($this->_layoutPath!==null) + return $this->_layoutPath; + else + return $this->_layoutPath=$this->getViewPath().DIRECTORY_SEPARATOR.'layouts'; + } + + /** + * @param string the root directory of layout files. + * @throws CException if the directory does not exist. + */ + public function setLayoutPath($path) + { + if(($this->_layoutPath=realpath($path))===false || !is_dir($this->_layoutPath)) + throw new CException(Yii::t('yii','The layout path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * The pre-filter for controller actions. + * This method is invoked before the currently requested controller action and all its filters + * are executed. You may override this method with logic that needs to be done + * before all controller actions. + * @param CController the controller + * @param CAction the action + * @return boolean whether the action should be executed. + * @since 1.0.4 + */ + public function beforeControllerAction($controller,$action) + { + return true; + } + + /** + * The post-filter for controller actions. + * This method is invoked after the currently requested controller action and all its filters + * are executed. You may override this method with logic that needs to be done + * after all controller actions. + * @param CController the controller + * @param CAction the action + * @since 1.0.4 + */ + public function afterControllerAction($controller,$action) + { + } + + /** + * Searches for a module by its ID. + * This method is used internally. Do not call this method. + * @param string module ID + * @return CWebModule the module that has the specified ID. Null if no module is found. + * @since 1.0.3 + */ + public function findModule($id) + { + if(($controller=$this->getController())!==null && ($module=$controller->getModule())!==null) + { + do + { + if(($m=$module->getModule($id))!==null) + return $m; + } while(($module=$module->getParentModule())!==null); + } + if(($m=$this->getModule($id))!==null) + return $m; + } + + /** + * Initializes the application. + * This method overrides the parent implementation by preloading the 'request' component. + */ + protected function init() + { + parent::init(); + // preload 'request' so that it has chance to respond to onBeginRequest event. + $this->getRequest(); + } +} diff --git a/web/framework-1.0.8/web/CWebModule.php b/web/framework-1.0.8/web/CWebModule.php new file mode 100644 index 0000000..448d793 --- /dev/null +++ b/web/framework-1.0.8/web/CWebModule.php @@ -0,0 +1,187 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebModule represents an application module. + * + * An application module may be considered as a self-contained sub-application + * that has its own controllers, models and views and can be reused in a different + * project as a whole. Controllers inside a module must be accessed with routes + * that are prefixed with the module ID. + * + * @author Qiang Xue + * @version $Id: CWebModule.php 870 2009-03-22 15:06:23Z qiang.xue $ + * @package system.web + * @since 1.0.3 + */ +class CWebModule extends CModule +{ + /** + * @var string the ID of the default controller for this module. Defaults to 'default'. + */ + public $defaultController='default'; + /** + * @var mixed the layout that is shared by the controllers inside this module. + * If a controller has explicitly declared its own {@link CController::layout layout}, + * this property will be ignored. + * If this is null (default), the application's layout or the parent module's layout (if available) + * will be used. If this is false, then no layout will be used. + */ + public $layout; + /** + * @var array mapping from controller ID to controller configurations. + * Pleaser refer to {@link CWebApplication::controllerMap} for more details. + */ + public $controllerMap=array(); + + private $_controllerPath; + private $_viewPath; + private $_layoutPath; + + + /** + * Returns the name of this module. + * The default implementation simply returns {@link id}. + * You may override this method to customize the name of this module. + * @return string the name of this module. + */ + public function getName() + { + return basename($this->getId()); + } + + /** + * Returns the description of this module. + * The default implementation returns an empty string. + * You may override this method to customize the description of this module. + * @return string the description of this module. + */ + public function getDescription() + { + return ''; + } + + /** + * Returns the version of this module. + * The default implementation returns '1.0'. + * You may override this method to customize the version of this module. + * @return string the version of this module. + */ + public function getVersion() + { + return '1.0'; + } + + /** + * @return string the directory that contains the controller classes. Defaults to 'protected/controllers'. + */ + public function getControllerPath() + { + if($this->_controllerPath!==null) + return $this->_controllerPath; + else + return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'controllers'; + } + + /** + * @param string the directory that contains the controller classes. + * @throws CException if the directory is invalid + */ + public function setControllerPath($value) + { + if(($this->_controllerPath=realpath($value))===false || !is_dir($this->_controllerPath)) + throw new CException(Yii::t('yii','The controller path "{path}" is not a valid directory.', + array('{path}'=>$value))); + } + + /** + * @return string the root directory of view files. Defaults to 'protected/views'. + */ + public function getViewPath() + { + if($this->_viewPath!==null) + return $this->_viewPath; + else + return $this->_viewPath=$this->getBasePath().DIRECTORY_SEPARATOR.'views'; + } + + /** + * @param string the root directory of view files. + * @throws CException if the directory does not exist. + */ + public function setViewPath($path) + { + if(($this->_viewPath=realpath($path))===false || !is_dir($this->_viewPath)) + throw new CException(Yii::t('yii','The view path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * @return string the root directory of layout files. Defaults to 'protected/views/layouts'. + */ + public function getLayoutPath() + { + if($this->_layoutPath!==null) + return $this->_layoutPath; + else + return $this->_layoutPath=$this->getViewPath().DIRECTORY_SEPARATOR.'layouts'; + } + + /** + * @param string the root directory of layout files. + * @throws CException if the directory does not exist. + */ + public function setLayoutPath($path) + { + if(($this->_layoutPath=realpath($path))===false || !is_dir($this->_layoutPath)) + throw new CException(Yii::t('yii','The layout path "{path}" is not a valid directory.', + array('{path}'=>$path))); + } + + /** + * The pre-filter for controller actions. + * This method is invoked before the currently requested controller action and all its filters + * are executed. You may override this method in the following way: + *
    +	 * if(parent::beforeControllerAction($controller,$action))
    +	 * {
    +	 *     // your code
    +	 *     return true;
    +	 * }
    +	 * else
    +	 *     return false;
    +	 * 
    + * @param CController the controller + * @param CAction the action + * @return boolean whether the action should be executed. + * @since 1.0.4 + */ + public function beforeControllerAction($controller,$action) + { + if(($parent=$this->getParentModule())===null) + $parent=Yii::app(); + return $parent->beforeControllerAction($controller,$action); + } + + /** + * The post-filter for controller actions. + * This method is invoked after the currently requested controller action and all its filters + * are executed. If you override this method, make sure you call the parent implementation at the end. + * @param CController the controller + * @param CAction the action + * @since 1.0.4 + */ + public function afterControllerAction($controller,$action) + { + if(($parent=$this->getParentModule())===null) + $parent=Yii::app(); + $parent->afterControllerAction($controller,$action); + } +} diff --git a/web/framework-1.0.8/web/actions/CAction.php b/web/framework-1.0.8/web/actions/CAction.php new file mode 100644 index 0000000..56a05a6 --- /dev/null +++ b/web/framework-1.0.8/web/actions/CAction.php @@ -0,0 +1,58 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAction is the base class for all controller action classes. + * + * CAction provides a way to divide a complex controller into + * smaller actions in separate class files. + * + * Derived classes must implement {@link run()} which is invoked by + * controller when the action is requested. + * + * An action instance can access its controller via {@link getController controller} property. + * + * @author Qiang Xue + * @version $Id: CAction.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.actions + * @since 1.0 + */ +abstract class CAction extends CComponent implements IAction +{ + private $_id; + private $_controller; + + /** + * Constructor. + * @param CController the controller who owns this action. + * @param string id of the action. + */ + public function __construct($controller,$id) + { + $this->_controller=$controller; + $this->_id=$id; + } + + /** + * @return CController the controller who owns this action. + */ + public function getController() + { + return $this->_controller; + } + + /** + * @return string id of this action + */ + public function getId() + { + return $this->_id; + } +} diff --git a/web/framework-1.0.8/web/actions/CInlineAction.php b/web/framework-1.0.8/web/actions/CInlineAction.php new file mode 100644 index 0000000..1f78888 --- /dev/null +++ b/web/framework-1.0.8/web/actions/CInlineAction.php @@ -0,0 +1,34 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CInlineAction represents an action that is defined as a controller method. + * + * The method name is like 'actionXYZ' where 'XYZ' stands for the action name. + * + * @author Qiang Xue + * @version $Id: CInlineAction.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.actions + * @since 1.0 + */ +class CInlineAction extends CAction +{ + /** + * Runs the action. + * The action method defined in the controller is invoked. + * This method is required by {@link CAction}. + */ + public function run() + { + $method='action'.$this->getId(); + $this->getController()->$method(); + } +} diff --git a/web/framework-1.0.8/web/actions/CViewAction.php b/web/framework-1.0.8/web/actions/CViewAction.php new file mode 100644 index 0000000..8c89829 --- /dev/null +++ b/web/framework-1.0.8/web/actions/CViewAction.php @@ -0,0 +1,165 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CViewAction represents an action that displays a view according to a user-specified parameter. + * + * By default, the view being displayed is specified via the view GET parameter. + * The name of the GET parameter can be customized via {@link viewParam}. + * If the user doesn't provide the GET parameter, the default view specified by {@link defaultView} + * will be displayed. + * + * Users specify a view in the format of path.to.view, which translates to the view name + * BasePath/path/to/view where BasePath is given by {@link basePath}. + * + * Note, the user specified view can only contain word characters, dots and dashes and + * the first letter must be a word letter. + * + * @author Qiang Xue + * @version $Id: CViewAction.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.actions + * @since 1.0 + */ +class CViewAction extends CAction +{ + /** + * @var string the name of the GET parameter that contains the requested view name. Defaults to 'view'. + */ + public $viewParam='view'; + /** + * @var string the name of the default view when {@link viewParam} GET parameter is not provided by user. Defaults to 'index'. + * This should be in the format of 'path.to.view', similar to that given in + * the GET parameter. + * @see basePath + */ + public $defaultView='index'; + /** + * @var string the name of the view to be rendered. This property will be set + * once the user requested view is resolved. + */ + public $view; + /** + * @var string the base path for the views. Defaults to 'pages'. + * The base path will be prefixed to any user-specified page view. + * For example, if a user requests for tutorial.chap1, the corresponding view name will + * be pages/tutorial/chap1, assuming the base path is pages. + * The actual view file is determined by {@link CController::getViewFile}. + * @see CController::getViewFile + */ + public $basePath='pages'; + /** + * @var mixed the name of the layout to be applied to the views. + * This will be assigned to {@link CController::layout} before the view is rendered. + * Defaults to null, meaning the controller's layout will be used. + * If false, no layout will be applied. + */ + public $layout; + /** + * @var boolean whether the view should be rendered as PHP script or static text. Defaults to false. + */ + public $renderAsText=false; + + private $_viewPath; + + + /** + * Returns the name of the view requested by the user. + * If the user doesn't specify any view, the {@link defaultView} will be returned. + * @return string the name of the view requested by the user. + * This is in the format of 'path.to.view'. + */ + public function getRequestedView() + { + if($this->_viewPath===null) + { + if(isset($_GET[$this->viewParam]) && !empty($_GET[$this->viewParam])) + $this->_viewPath=$_GET[$this->viewParam]; + else + $this->_viewPath=$this->defaultView; + } + return $this->_viewPath; + } + + /** + * Resolves the user-specified view into a valid view name. + * @param string user-specified view in the format of 'path.to.view'. + * @return string fully resolved view in the format of 'path/to/view'. + * @throw CHttpException if the user-specified view is invalid + */ + protected function resolveView($viewPath) + { + // start with a word char and have word chars, dots and dashes only + if(preg_match('/^\w[\w\.\-]*$/',$viewPath)) + { + $view=strtr($viewPath,'.','/'); + if(!empty($this->basePath)) + $view=$this->basePath.'/'.$view; + if($this->getController()->getViewFile($view)!==false) + { + $this->view=$view; + return; + } + } + throw new CHttpException(404,Yii::t('yii','The requested view "{name}" is not found.', + array('{name}'=>$viewPath))); + } + + /** + * Runs the action. + * This method displays the view requested by the user. + * @throws CHttpException if the view is invalid + */ + public function run() + { + $this->resolveView($this->getRequestedView()); + $controller=$this->getController(); + if($this->layout!==null) + { + $layout=$controller->layout; + $controller->layout=$this->layout; + } + + $this->onBeforeRender($event=new CEvent($this)); + if(!$event->handled) + { + if($this->renderAsText) + { + $text=file_get_contents($controller->getViewFile($this->view)); + $controller->renderText($text); + } + else + $controller->render($this->view); + $this->onAfterRender(new CEvent($this)); + } + + if($this->layout!==null) + $controller->layout=$layout; + } + + /** + * Raised right before the action invokes the render method. + * Event handlers can set the {@link CEvent::handled} property + * to be true to stop further view rendering. + * @param CEvent event parameter + */ + public function onBeforeRender($event) + { + $this->raiseEvent('onBeforeRender',$event); + } + + /** + * Raised right after the action invokes the render method. + * @param CEvent event parameter + */ + public function onAfterRender($event) + { + $this->raiseEvent('onAfterRender',$event); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/auth/CAccessControlFilter.php b/web/framework-1.0.8/web/auth/CAccessControlFilter.php new file mode 100644 index 0000000..4e051c5 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CAccessControlFilter.php @@ -0,0 +1,295 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAccessControlFilter performs authorization checks for the specified actions. + * + * By enabling this filter, controller actions can be checked for access permissions. + * Only when the user is allowed by one of the security rules, will he be able + * to access the action. + * + * To specify the access rules, set the {@link setRules rules} property, which should + * be an array of the rules. Each rule is specified as an array of the following structure: + *
    + * array(
    + *   'allow',  // or 'deny'
    + *   // optional, list of action IDs (case insensitive) that this rule applies to
    + *   'actions'=>array('edit', 'delete'),
    + *   // optional, list of controller IDs (case insensitive) that this rule applies to
    + *   // This option is available since version 1.0.3.
    + *   'controllers'=>array('post', 'admin/user'),
    + *   // optional, list of usernames (case insensitive) that this rule applies to
    + *   // Use * to represent all users, ? guest users, and @ authenticated users
    + *   'users'=>array('thomas', 'kevin'),
    + *   // optional, list of roles (case sensitive!) that this rule applies to.
    + *   'roles'=>array('admin', 'editor'),
    + *   // optional, list of IP address/patterns that this rule applies to
    + *   // e.g. 127.0.0.1, 127.0.0.*
    + *   'ips'=>array('127.0.0.1'),
    + *   // optional, list of request types (case insensitive) that this rule applies to
    + *   'verbs'=>array('GET', 'POST'),
    + *   // optional, a PHP expression whose value indicates whether this rule applies
    + *   // This option is available since version 1.0.3.
    + *   'expression'=>'!$user->isGuest && $user->level==2',
    + * )
    + * 
    + * + * @author Qiang Xue + * @version $Id: CAccessControlFilter.php 1280 2009-07-25 11:16:36Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CAccessControlFilter extends CFilter +{ + private $_rules=array(); + + /** + * @return array list of access rules. + */ + public function getRules() + { + return $this->_rules; + } + + /** + * @param array list of access rules. + */ + public function setRules($rules) + { + foreach($rules as $rule) + { + if(is_array($rule) && isset($rule[0])) + { + $r=new CAccessRule; + $r->allow=$rule[0]==='allow'; + foreach(array_slice($rule,1) as $name=>$value) + { + if($name==='expression' || $name==='roles') + $r->$name=$value; + else + $r->$name=array_map('strtolower',$value); + } + $this->_rules[]=$r; + } + } + } + + /** + * Performs the pre-action filtering. + * @param CFilterChain the filter chain that the filter is on. + * @return boolean whether the filtering process should continue and the action + * should be executed. + */ + protected function preFilter($filterChain) + { + $app=Yii::app(); + $request=$app->getRequest(); + $user=$app->getUser(); + $verb=$request->getRequestType(); + $ip=$request->getUserHostAddress(); + + foreach($this->getRules() as $rule) + { + if(($allow=$rule->isUserAllowed($user,$filterChain->controller,$filterChain->action,$ip,$verb))>0) // allowed + break; + else if($allow<0) // denied + { + $this->accessDenied($user); + return false; + } + } + + return true; + } + + /** + * Denies the access of the user. + * This method is invoked when access check fails. + * @param IWebUser the current user + * @since 1.0.5 + */ + protected function accessDenied($user) + { + if($user->getIsGuest()) + $user->loginRequired(); + else + throw new CHttpException(403,Yii::t('yii','You are not authorized to perform this action.')); + } +} + + +/** + * CAccessRule represents an access rule that is managed by {@link CAccessControlFilter}. + * + * @author Qiang Xue + * @version $Id: CAccessControlFilter.php 1280 2009-07-25 11:16:36Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CAccessRule extends CComponent +{ + /** + * @var boolean whether this is an 'allow' rule or 'deny' rule. + */ + public $allow; + /** + * @var array list of action IDs that this rule applies to. The comparison is case-insensitive. + */ + public $actions; + /** + * @var array list of controler IDs that this rule applies to. The comparison is case-insensitive. + * @since 1.0.4 + */ + public $controllers; + /** + * @var array list of user names that this rule applies to. The comparison is case-insensitive. + */ + public $users; + /** + * @var array list of roles this rule applies to. For each role, the current user's + * {@link CWebUser::checkAccess} method will be invoked. If one of the invocations + * returns true, the rule will be applied. + * Note, you should mainly use roles in an "allow" rule because by definition, + * a role represents a permission collection. + * @see CAuthManager + */ + public $roles; + /** + * @var array IP patterns. + */ + public $ips; + /** + * @var array list of request types (e.g. GET, POST) that this rule applies to. + */ + public $verbs; + /** + * @var string a PHP expression whose value indicates whether this rule should be applied. + * In this expression, you can use $user which refers to Yii::app()->user. + * @since 1.0.3 + */ + public $expression; + + + /** + * Checks whether the Web user is allowed to perform the specified action. + * @param CWebUser the user object + * @param CController the controller currently being executed + * @param CAction the action to be performed + * @param string the request IP address + * @param string the request verb (GET, POST, etc.) + * @return integer 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user + */ + public function isUserAllowed($user,$controller,$action,$ip,$verb) + { + if($this->isActionMatched($action) + && $this->isUserMatched($user) + && $this->isRoleMatched($user) + && $this->isIpMatched($ip) + && $this->isVerbMatched($verb) + && $this->isControllerMatched($controller) + && $this->isExpressionMatched($user)) + return $this->allow ? 1 : -1; + else + return 0; + } + + /** + * @param CAction the action + * @return boolean whether the rule applies to the action + */ + protected function isActionMatched($action) + { + return empty($this->actions) || in_array(strtolower($action->getId()),$this->actions); + } + + /** + * @param CAction the action + * @return boolean whether the rule applies to the action + */ + protected function isControllerMatched($controller) + { + return empty($this->controllers) || in_array(strtolower($controller->getId()),$this->controllers); + } + + /** + * @param IWebUser the user + * @return boolean whether the rule applies to the user + */ + protected function isUserMatched($user) + { + if(empty($this->users)) + return true; + foreach($this->users as $u) + { + if($u==='*') + return true; + else if($u==='?' && $user->getIsGuest()) + return true; + else if($u==='@' && !$user->getIsGuest()) + return true; + else if(!strcasecmp($u,$user->getName())) + return true; + } + return false; + } + + /** + * @param string the role name + * @return boolean whether the rule applies to the role + */ + protected function isRoleMatched($user) + { + if(empty($this->roles)) + return true; + foreach($this->roles as $role) + { + if($user->checkAccess($role)) + return true; + } + return false; + } + + /** + * @param string the IP address + * @return boolean whether the rule applies to the IP address + */ + protected function isIpMatched($ip) + { + if(empty($this->ips)) + return true; + foreach($this->ips as $rule) + { + if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && !strncmp($ip,$rule,$pos))) + return true; + } + return false; + } + + /** + * @param string the request method + * @return boolean whether the rule applies to the request + */ + protected function isVerbMatched($verb) + { + return empty($this->verbs) || in_array(strtolower($verb),$this->verbs); + } + + /** + * @param IWebUser the user + * @return boolean the expression value. True if the expression is not specified. + * @since 1.0.3 + */ + protected function isExpressionMatched($user) + { + if($this->expression===null) + return true; + return @eval('return '.$this->expression.';'); + } +} diff --git a/web/framework-1.0.8/web/auth/CAuthAssignment.php b/web/framework-1.0.8/web/auth/CAuthAssignment.php new file mode 100644 index 0000000..edeb647 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CAuthAssignment.php @@ -0,0 +1,102 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAuthAssignment represents an assignment of a role to a user. + * It includes additional assignment information such as {@link bizRule} and {@link data}. + * Do not create a CAuthAssignment instance using the 'new' operator. + * Instead, call {@link IAuthManager::assign}. + * + * @author Qiang Xue + * @version $Id: CAuthAssignment.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CAuthAssignment extends CComponent +{ + private $_auth; + private $_itemName; + private $_userId; + private $_bizRule; + private $_data; + + /** + * Constructor. + * @param IAuthManager the authorization manager + * @param string authorization item name + * @param mixed user ID (see {@link IWebUser::getId}) + * @param string the business rule associated with this assignment + * @param mixed additional data for this assignment + */ + public function __construct($auth,$itemName,$userId,$bizRule=null,$data=null) + { + $this->_auth=$auth; + $this->_itemName=$itemName; + $this->_userId=$userId; + $this->_bizRule=$bizRule; + $this->_data=$data; + } + + /** + * @return mixed user ID (see {@link IWebUser::getId}) + */ + public function getUserId() + { + return $this->_userId; + } + + /** + * @return string the authorization item name + */ + public function getItemName() + { + return $this->_itemName; + } + + /** + * @return string the business rule associated with this assignment + */ + public function getBizRule() + { + return $this->_bizRule; + } + + /** + * @param string the business rule associated with this assignment + */ + public function setBizRule($value) + { + if($this->_bizRule!==$value) + { + $this->_bizRule=$value; + $this->_auth->saveAuthAssignment($this); + } + } + + /** + * @return mixed additional data for this assignment + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed additional data for this assignment + */ + public function setData($value) + { + if($this->_data!==$value) + { + $this->_data=$value; + $this->_auth->saveAuthAssignment($this); + } + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/auth/CAuthItem.php b/web/framework-1.0.8/web/auth/CAuthItem.php new file mode 100644 index 0000000..b2dd550 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CAuthItem.php @@ -0,0 +1,269 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAuthItem represents an authorization item. + * An authorization item can be an operation, a task or a role. + * They form an authorization hierarchy. Items on higher levels of the hierarchy + * inherit the permissions represented by items on lower levels. + * A user may be assigned one or several authorization items (called {@link CAuthAssignment assignments}. + * He can perform an operation only when it is among his assigned items. + * + * @author Qiang Xue + * @version $Id: CAuthItem.php 971 2009-04-30 03:00:43Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CAuthItem extends CComponent +{ + const TYPE_OPERATION=0; + const TYPE_TASK=1; + const TYPE_ROLE=2; + + private $_auth; + private $_type; + private $_name; + private $_description; + private $_bizRule; + private $_data; + + /** + * Constructor. + * @param IAuthManager authorization manager + * @param string authorization item name + * @param integer authorization item type. This can be 0 (operation), 1 (task) or 2 (role). + * @param description the description + * @param string the business rule associated with this item + * @param mixed additional data for this item + */ + public function __construct($auth,$name,$type,$description='',$bizRule=null,$data=null) + { + $this->_type=$type; + $this->_auth=$auth; + $this->_name=$name; + $this->_description=$description; + $this->_bizRule=$bizRule; + $this->_data=$data; + } + + /** + * Checks to see if the specified item is within the hierarchy starting from this item. + * This method is internally used by {@link IAuthManager::checkAccess}. + * @param string the name of the item to be checked + * @param array the parameters to be passed to business rule evaluation + * @return boolean whether the specified item is within the hierarchy starting from this item. + */ + public function checkAccess($itemName,$params=array()) + { + Yii::trace('Checking permission "'.$this->_name.'"','system.web.auth.CAuthItem'); + if($this->_auth->executeBizRule($this->_bizRule,$params,$this->_data)) + { + if($this->_name==$itemName) + return true; + foreach($this->_auth->getItemChildren($this->_name) as $item) + { + if($item->checkAccess($itemName,$params)) + return true; + } + } + return false; + } + + /** + * @return IAuthManager the authorization manager + */ + public function getAuthManager() + { + return $this->_auth; + } + + /** + * @return integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role). + */ + public function getType() + { + return $this->_type; + } + + /** + * @return string the item name + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string the item name + */ + public function setName($value) + { + if($this->_name!==$value) + { + $oldName=$this->_name; + $this->_name=$value; + $this->_auth->saveAuthItem($this,$oldName); + } + } + + /** + * @return string the item description + */ + public function getDescription() + { + return $this->_description; + } + + /** + * @param string the item description + */ + public function setDescription($value) + { + if($this->_description!==$value) + { + $this->_description=$value; + $this->_auth->saveAuthItem($this); + } + } + + /** + * @return string the business rule associated with this item + */ + public function getBizRule() + { + return $this->_bizRule; + } + + /** + * @param string the business rule associated with this item + */ + public function setBizRule($value) + { + if($this->_bizRule!==$value) + { + $this->_bizRule=$value; + $this->_auth->saveAuthItem($this); + } + } + + /** + * @return string the additional data associated with this item + */ + public function getData() + { + return $this->_data; + } + + /** + * @param string the business rule associated with this item + */ + public function setData($value) + { + if($this->_data!==$value) + { + $this->_data=$value; + $this->_auth->saveAuthItem($this); + } + } + + /** + * Adds a child item. + * @param string the name of the child item + * @return boolean whether the item is added successfully + * @throws CException if either parent or child doesn't exist or if a loop has been detected. + * @see IAuthManager::addItemChild + */ + public function addChild($name) + { + return $this->_auth->addItemChild($this->_name,$name); + } + + /** + * Removes a child item. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string the child item name + * @return boolean whether the removal is successful + * @see IAuthManager::removeItemChild + */ + public function removeChild($name) + { + $this->_auth->removeItemChild($this->_name,$name); + } + + /** + * Returns a value indicating whether a child exists + * @param string the child item name + * @return boolean whether the child exists + * @see IAuthManager::hasItemChild + */ + public function hasChild($name) + { + return $this->_auth->hasItemChild($this->_name,$name); + } + + /** + * Returns the children of this item. + * @return array all child items of this item. + * @see IAuthManager::getItemChildren + */ + public function getChildren() + { + return $this->_auth->getItemChildren($this->_name); + } + + /** + * Assigns this item to a user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @param string the business rule to be executed when {@link checkAccess} is called + * for this particular authorization item. + * @param mixed additional data associated with this assignment + * @return CAuthAssignment the authorization assignment information. + * @throws CException if the item has already been assigned to the user + * @see IAuthManager::assign + */ + public function assign($userId,$bizRule=null,$data=null) + { + return $this->_auth->assign($this->_name,$userId,$bizRule,$data); + } + + /** + * Revokes an authorization assignment from a user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether removal is successful + * @see IAuthManager::revoke + */ + public function revoke($userId) + { + $this->_auth->revoke($this->_name,$userId); + } + + /** + * Returns a value indicating whether this item has been assigned to the user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether the item has been assigned to the user. + * @see IAuthManager::isAssigned + */ + public function isAssigned($userId) + { + return $this->_auth->isAssigned($this->_name,$userId); + } + + /** + * Returns the item assignment information. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return CAuthAssignment the item assignment information. Null is returned if + * this item is not assigned to the user. + * @see IAuthManager::getAuthAssignment + */ + public function getAssignment($userId) + { + return $this->_auth->getAuthAssignment($this->_name,$userId); + } +} diff --git a/web/framework-1.0.8/web/auth/CAuthManager.php b/web/framework-1.0.8/web/auth/CAuthManager.php new file mode 100644 index 0000000..1624df9 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CAuthManager.php @@ -0,0 +1,157 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAuthManager is the base class for authorization manager classes. + * + * CAuthManager extends {@link CApplicationComponent} and implements some methods + * that are common among authorization manager classes. + * + * CAuthManager together with its concrete child classes implement the Role-Based + * Access Control (RBAC). + * + * The main idea is that permissions are organized as a hierarchy of + * {@link CAuthItem authorization items}. Items on higer level inherit the permissions + * represented by items on lower level. And roles are simply top-level authorization items + * that may be assigned to individual users. A user is said to have a permission + * to do something if the corresponding authorization item is inherited by one of his roles. + * + * Using authorization manager consists of two aspects. First, the authorization hierarchy + * and assignments have to be established. CAuthManager and its child classes + * provides APIs to accomplish this task. Developers may need to develop some GUI + * so that it is more intuitive to end-users. Second, developers call {@link IAuthManager::checkAccess} + * at appropriate places in the application code to check if the current user + * has the needed permission for an operation. + * + * @author Qiang Xue + * @version $Id: CAuthManager.php 741 2009-02-24 19:36:22Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +abstract class CAuthManager extends CApplicationComponent implements IAuthManager +{ + /** + * @var array list of role names that are assigned to all users implicitly. + * These roles do not need to be explicitly assigned to any user. + * When calling {@link checkAccess}, these roles will be checked first. + * For performance reason, you should minimize the number of such roles. + * A typical usage of such roles is to define an 'authenticated' role and associate + * it with a biz rule which checks if the current user is authenticated. + * And then declare 'authenticated' in this property so that it can be applied to + * every authenticated user. + * @since 1.0.3 + */ + public $defaultRoles=array(); + + /** + * Creates a role. + * This is a shortcut method to {@link IAuthManager::createAuthItem}. + * @param string the item name + * @param string the item description. + * @param string the business rule associated with this item + * @param mixed additional data to be passed when evaluating the business rule + * @param CAuthItem the authorization item + */ + public function createRole($name,$description='',$bizRule=null,$data=null) + { + return $this->createAuthItem($name,CAuthItem::TYPE_ROLE,$description,$bizRule,$data); + } + + /** + * Creates a task. + * This is a shortcut method to {@link IAuthManager::createAuthItem}. + * @param string the item name + * @param string the item description. + * @param string the business rule associated with this item + * @param mixed additional data to be passed when evaluating the business rule + * @param CAuthItem the authorization item + */ + public function createTask($name,$description='',$bizRule=null,$data=null) + { + return $this->createAuthItem($name,CAuthItem::TYPE_TASK,$description,$bizRule,$data); + } + + /** + * Creates an operation. + * This is a shortcut method to {@link IAuthManager::createAuthItem}. + * @param string the item name + * @param string the item description. + * @param string the business rule associated with this item + * @param mixed additional data to be passed when evaluating the business rule + * @param CAuthItem the authorization item + */ + public function createOperation($name,$description='',$bizRule=null,$data=null) + { + return $this->createAuthItem($name,CAuthItem::TYPE_OPERATION,$description,$bizRule,$data); + } + + /** + * Returns roles. + * This is a shortcut method to {@link IAuthManager::getAuthItems}. + * @param mixed the user ID. If not null, only the roles directly assigned to the user + * will be returned. Otherwise, all roles will be returned. + * @return array roles (name=>CAuthItem) + */ + public function getRoles($userId=null) + { + return $this->getAuthItems(CAuthItem::TYPE_ROLE,$userId); + } + + /** + * Returns tasks. + * This is a shortcut method to {@link IAuthManager::getAuthItems}. + * @param mixed the user ID. If not null, only the tasks directly assigned to the user + * will be returned. Otherwise, all tasks will be returned. + * @return array tasks (name=>CAuthItem) + */ + public function getTasks($userId=null) + { + return $this->getAuthItems(CAuthItem::TYPE_TASK,$userId); + } + + /** + * Returns operations. + * This is a shortcut method to {@link IAuthManager::getAuthItems}. + * @param mixed the user ID. If not null, only the operations directly assigned to the user + * will be returned. Otherwise, all operations will be returned. + * @return array operations (name=>CAuthItem) + */ + public function getOperations($userId=null) + { + return $this->getAuthItems(CAuthItem::TYPE_OPERATION,$userId); + } + + /** + * Executes the specified business rule. + * @param string the business rule to be executed. + * @param array parameters passed to {@link IAuthManager::checkAccess}. + * @param mixed additional data associated with the authorization item or assignment. + * @return boolean whether the business rule returns true. + * If the business rule is empty, it will still return true. + */ + public function executeBizRule($bizRule,$params,$data) + { + return empty($bizRule) || @eval($bizRule)!=0; + } + + /** + * Checks the item types to make sure a child can be added to a parent. + * @param integer parent item type + * @param integer child item type + * @throws CException if the item cannot be added as a child due to its incompatible type. + */ + protected function checkItemChildType($parentType,$childType) + { + static $types=array('operation','task','role'); + if($parentType < $childType) + throw new CException(Yii::t('yii','Cannot add an item of type "{child}" to an item of type "{parent}".', + array('{child}'=>$types[$childType], '{parent}'=>$types[$parentType]))); + } +} diff --git a/web/framework-1.0.8/web/auth/CBaseUserIdentity.php b/web/framework-1.0.8/web/auth/CBaseUserIdentity.php new file mode 100644 index 0000000..017f830 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CBaseUserIdentity.php @@ -0,0 +1,116 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CBaseUserIdentity is a base class implementing {@link IUserIdentity}. + * + * CBaseUserIdentity implements the scheme for representing identity + * information that needs to be persisted. It also provides the way + * to represent the authentication errors. + * + * Derived classes should implement {@link IUserIdentity::authenticate} + * and {@link IUserIdentity::getId} that are required by the {@link IUserIdentity} + * interface. + * + * @author Qiang Xue + * @version $Id: CBaseUserIdentity.php 1286 2009-08-06 03:32:04Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +abstract class CBaseUserIdentity extends CComponent implements IUserIdentity +{ + const ERROR_NONE=0; + const ERROR_USERNAME_INVALID=1; + const ERROR_PASSWORD_INVALID=2; + const ERROR_UNKNOWN_IDENTITY=100; + + /** + * @var integer the authentication error code. If there is an error, the error code will be non-zero. + * Defaults to 100, meaning unknown identity. Calling {@link authenticate} will change this value. + */ + public $errorCode=self::ERROR_UNKNOWN_IDENTITY; + /** + * @var string the authentication error message. Defaults to empty. + */ + public $errorMessage=''; + + private $_state=array(); + + /** + * Returns a value that uniquely represents the identity. + * @return mixed a value that uniquely represents the identity (e.g. primary key value). + * The default implementation simply returns {@link name}. + */ + public function getId() + { + return $this->getName(); + } + + /** + * Returns the display name for the identity (e.g. username). + * @return string the display name for the identity. + * The default implementation simply returns empty string. + */ + public function getName() + { + return ''; + } + + /** + * Returns the identity states that should be persisted. + * This method is required by {@link IUserIdentity}. + * @return array the identity states that should be persisted. + */ + public function getPersistentStates() + { + return $this->_state; + } + + /** + * Returns a value indicating whether the identity is authenticated. + * This method is required by {@link IUserIdentity}. + * @return whether the authentication is successful. + */ + public function getIsAuthenticated() + { + return $this->errorCode==self::ERROR_NONE; + } + + /** + * Gets the persisted state by the specified name. + * @param string the name of the state + * @param mixed the default value to be returned if the named state does not exist + * @return mixed the value of the named state + */ + public function getState($name,$defaultValue=null) + { + return isset($this->_state[$name])?$this->_state[$name]:$defaultValue; + } + + /** + * Sets the named state with a given value. + * @param string the name of the state + * @param mixed the value of the named state + */ + public function setState($name,$value) + { + $this->_state[$name]=$value; + } + + /** + * Removes the specified state. + * @param string the name of the state + * @since 1.0.8 + */ + public function clearState($name) + { + unset($this->_state[$name]); + } +} diff --git a/web/framework-1.0.8/web/auth/CDbAuthManager.php b/web/framework-1.0.8/web/auth/CDbAuthManager.php new file mode 100644 index 0000000..25414b8 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CDbAuthManager.php @@ -0,0 +1,584 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbAuthManager represents an authorization manager that stores authorization information in database. + * + * The database connection is specified by {@link connectionID}. And the database schema + * should be as described in "framework/web/auth/schema.sql". You may change the names of + * the three tables used to store the authorization data by setting {@link itemTable}, + * {@link itemChildTable} and {@link assignmentTable}. + * + * @author Qiang Xue + * @version $Id: CDbAuthManager.php 970 2009-04-30 02:59:02Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CDbAuthManager extends CAuthManager +{ + /** + * @var string the ID of the {@link CDbConnection} application component. Defaults to 'db'. + * The database must have the tables as declared in "framework/web/auth/schema.sql". + */ + public $connectionID='db'; + /** + * @var string the name of the table storing authorization items. Defaults to 'AuthItem'. + */ + public $itemTable='AuthItem'; + /** + * @var string the name of the table storing authorization item hierarchy. Defaults to 'AuthItemChild'. + */ + public $itemChildTable='AuthItemChild'; + /** + * @var string the name of the table storing authorization item assignments. Defaults to 'AuthAssignment'. + */ + public $assignmentTable='AuthAssignment'; + /** + * @var CDbConnection the database connection. By default, this is initialized + * automatically as the application component whose ID is indicated as {@link connectionID}. + */ + public $db; + + private $_usingSqlite; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->db!==null) + $this->db->setActive(false); + } + + /** + * Initializes the application component. + * This method overrides the parent implementation by establishing the database connection. + */ + public function init() + { + parent::init(); + + $this->getDbConnection()->setActive(true); + $this->_usingSqlite=!strncmp($this->getDbConnection()->getDriverName(),'sqlite',6); + } + + /** + * Performs access check for the specified user. + * @param string the name of the operation that need access check + * @param mixed the user ID. This should can be either an integer and a string representing + * the unique identifier of a user. See {@link IWebUser::getId}. + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user. + */ + public function checkAccess($itemName,$userId,$params=array()) + { + if(!empty($this->defaultRoles) && $this->checkDefaultRoles($itemName,$params)) + return true; + + $sql="SELECT name, type, description, t1.bizrule, t1.data, t2.bizrule AS bizrule2, t2.data AS data2 FROM {$this->itemTable} t1, {$this->assignmentTable} t2 WHERE name=itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':userid',$userId); + + // check directly assigned items + $names=array(); + foreach($command->queryAll() as $row) + { + Yii::trace('Checking permission "'.$row['name'].'"','system.web.auth.CDbAuthManager'); + if($this->executeBizRule($row['bizrule2'],$params,unserialize($row['data2'])) + && $this->executeBizRule($row['bizrule'],$params,unserialize($row['data']))) + { + if($row['name']===$itemName) + return true; + $names[]=$row['name']; + } + } + + // check all descendant items + while($names!==array()) + { + $items=$this->getItemChildren($names); + $names=array(); + foreach($items as $item) + { + Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CDbAuthManager'); + if($this->executeBizRule($item->getBizRule(),$params,$item->getData())) + { + if($item->getName()===$itemName) + return true; + $names[]=$item->getName(); + } + } + } + + return false; + } + + + /** + * Checks the access based on the default roles as declared in {@link defaultRoles}. + * @param string the name of the operation that need access check + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user according to the default roles. + * @since 1.0.3 + */ + protected function checkDefaultRoles($itemName,$params) + { + $names=array(); + foreach($this->defaultRoles as $role) + { + if(is_string($role)) + $names[]=$this->db->quoteValue($role); + else + $names[]=$role; + } + if(count($names)<4) + $condition='name='.implode(' OR name=',$names); + else + $condition='name IN ('.implode(', ',$names).')'; + $sql="SELECT name, type, description, bizrule, data FROM {$this->itemTable} WHERE $condition"; + $command=$this->db->createCommand($sql); + $rows=$command->queryAll(); + + foreach($rows as $row) + { + Yii::trace('Checking default role "'.$row['name'].'"','system.web.auth.CDbAuthManager'); + $item=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],unserialize($row['data'])); + if($item->checkAccess($itemName,$params)) + return true; + } + return false; + } + + /** + * Adds an item as a child of another item. + * @param string the parent item name + * @param string the child item name + * @throws CException if either parent or child doesn't exist or if a loop has been detected. + */ + public function addItemChild($itemName,$childName) + { + if($itemName===$childName) + throw new CException(Yii::t('yii','Cannot add "{name}" as a child of itself.', + array('{name}'=>$itemName))); + $sql="SELECT * FROM {$this->itemTable} WHERE name=:name1 OR name=:name2"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name1',$itemName); + $command->bindValue(':name2',$childName); + $rows=$command->queryAll(); + if(count($rows)==2) + { + static $types=array('operation','task','role'); + if($rows[0]['name']===$itemName) + { + $parentType=$rows[0]['type']; + $childType=$rows[1]['type']; + } + else + { + $childType=$rows[0]['type']; + $parentType=$rows[1]['type']; + } + $this->checkItemChildType($parentType,$childType); + if($this->detectLoop($itemName,$childName)) + throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{name}". A loop has been detected.', + array('{child}'=>$childName,'{name}'=>$itemName))); + + $sql="INSERT INTO {$this->itemChildTable} (parent,child) VALUES (:parent,:child)"; + $command=$this->db->createCommand($sql); + $command->bindValue(':parent',$itemName); + $command->bindValue(':child',$childName); + $command->execute(); + } + else + throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{parent}'=>$itemName))); + } + + /** + * Removes a child from its parent. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the removal is successful + */ + public function removeItemChild($itemName,$childName) + { + $sql="DELETE FROM {$this->itemChildTable} WHERE parent=:parent AND child=:child"; + $command=$this->db->createCommand($sql); + $command->bindValue(':parent',$itemName); + $command->bindValue(':child',$childName); + return $command->execute()>0; + } + + /** + * Returns a value indicating whether a child exists within a parent. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the child exists + */ + public function hasItemChild($itemName,$childName) + { + $sql="SELECT parent FROM {$this->itemChildTable} WHERE parent=:parent AND child=:child"; + $command=$this->db->createCommand($sql); + $command->bindValue(':parent',$itemName); + $command->bindValue(':child',$childName); + return $command->queryScalar()!==false; + } + + /** + * Returns the children of the specified item. + * @param mixed the parent item name. This can be either a string or an array. + * The latter represents a list of item names (available since version 1.0.5). + * @return array all child items of the parent + */ + public function getItemChildren($names) + { + if(is_string($names)) + $condition='parent='.$this->db->quoteValue($names); + else if(is_array($names) && $names!==array()) + { + foreach($names as &$name) + $name=$this->db->quoteValue($name); + $condition='parent IN ('.implode(', ',$names).')'; + } + $sql="SELECT name, type, description, bizrule, data FROM {$this->itemTable}, {$this->itemChildTable} WHERE $condition AND name=child"; + $children=array(); + foreach($this->db->createCommand($sql)->queryAll() as $row) + $children[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],unserialize($row['data'])); + return $children; + } + + /** + * Assigns an authorization item to a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @param string the business rule to be executed when {@link checkAccess} is called + * for this particular authorization item. + * @param mixed additional data associated with this assignment + * @return CAuthAssignment the authorization assignment information. + * @throws CException if the item does not exist or if the item has already been assigned to the user + */ + public function assign($itemName,$userId,$bizRule=null,$data=null) + { + if($this->usingSqlite() && $this->getAuthItem($itemName)===null) + throw new CException(Yii::t('yii','The item "{name}" does not exist.',array('{name}'=>$itemName))); + + $sql="INSERT INTO {$this->assignmentTable} (itemname,userid,bizrule,data) VALUES (:itemname,:userid,:bizrule,:data)"; + $command=$this->db->createCommand($sql); + $command->bindValue(':itemname',$itemName); + $command->bindValue(':userid',$userId); + $command->bindValue(':bizrule',$bizRule); + $command->bindValue(':data',serialize($data)); + $command->execute(); + return new CAuthAssignment($this,$itemName,$userId,$bizRule,$data); + } + + /** + * Revokes an authorization assignment from a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether removal is successful + */ + public function revoke($itemName,$userId) + { + $sql="DELETE FROM {$this->assignmentTable} WHERE itemname=:itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':itemname',$itemName); + $command->bindValue(':userid',$userId); + return $command->execute()>0; + } + + /** + * Returns a value indicating whether the item has been assigned to the user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether the item has been assigned to the user. + */ + public function isAssigned($itemName,$userId) + { + $sql="SELECT itemname FROM {$this->assignmentTable} WHERE itemname=:itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':itemname',$itemName); + $command->bindValue(':userid',$userId); + return $command->queryScalar()!==false; + } + + /** + * Returns the item assignment information. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return CAuthAssignment the item assignment information. Null is returned if + * the item is not assigned to the user. + */ + public function getAuthAssignment($itemName,$userId) + { + $sql="SELECT * FROM {$this->assignmentTable} WHERE itemname=:itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':itemname',$itemName); + $command->bindValue(':userid',$userId); + if(($row=$command->queryRow($sql))!==false) + return new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],unserialize($row['data'])); + else + return null; + } + + /** + * Returns the item assignments for the specified user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return array the item assignment information for the user. An empty array will be + * returned if there is no item assigned to the user. + */ + public function getAuthAssignments($userId) + { + $sql="SELECT * FROM {$this->assignmentTable} WHERE userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':userid',$userId); + $assignments=array(); + foreach($command->queryAll($sql) as $row) + $assignments[$row['itemname']]=new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],unserialize($row['data'])); + return $assignments; + } + + /** + * Saves the changes to an authorization assignment. + * @param CAuthAssignment the assignment that has been changed. + */ + public function saveAuthAssignment($assignment) + { + $sql="UPDATE {$this->assignmentTable} SET bizrule=:bizrule, data=:data WHERE itemname=:itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':bizrule',$assignment->getBizRule()); + $command->bindValue(':data',serialize($assignment->getData())); + $command->bindValue(':itemname',$assignment->getItemName()); + $command->bindValue(':userid',$assignment->getUserId()); + $command->execute(); + } + + /** + * Returns the authorization items of the specific type and user. + * @param integer the item type (0: operation, 1: task, 2: role). Defaults to null, + * meaning returning all items regardless of their type. + * @param mixed the user ID. Defaults to null, meaning returning all items even if + * they are not assigned to a user. + * @return array the authorization items of the specific type. + */ + public function getAuthItems($type=null,$userId=null) + { + if($type===null && $userId===null) + { + $sql="SELECT * FROM {$this->itemTable}"; + $command=$this->db->createCommand($sql); + } + else if($userId===null) + { + $sql="SELECT * FROM {$this->itemTable} WHERE type=:type"; + $command=$this->db->createCommand($sql); + $command->bindValue(':type',$type); + } + else if($type===null) + { + $sql="SELECT name,type,description,t1.bizrule,t1.data + FROM {$this->itemTable} t1, {$this->assignmentTable} t2 + WHERE name=itemname AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':userid',$userId); + } + else + { + $sql="SELECT name,type,description,t1.bizrule,t1.data + FROM {$this->itemTable} t1, {$this->assignmentTable} t2 + WHERE name=itemname AND type=:type AND userid=:userid"; + $command=$this->db->createCommand($sql); + $command->bindValue(':type',$type); + $command->bindValue(':userid',$userId); + } + $items=array(); + foreach($command->queryAll() as $row) + $items[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],unserialize($row['data'])); + return $items; + } + + /** + * Creates an authorization item. + * An authorization item represents an action permission (e.g. creating a post). + * It has three types: operation, task and role. + * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * by lower level items. + * @param string the item name. This must be a unique identifier. + * @param integer the item type (0: operation, 1: task, 2: role). + * @param string description of the item + * @param string business rule associated with the item. This is a piece of + * PHP code that will be executed when {@link checkAccess} is called for the item. + * @param mixed additional data associated with the item. + * @return CAuthItem the authorization item + * @throws CException if an item with the same name already exists + */ + public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null) + { + $sql="INSERT INTO {$this->itemTable} (name,type,description,bizrule,data) VALUES (:name,:type,:description,:bizrule,:data)"; + $command=$this->db->createCommand($sql); + $command->bindValue(':type',$type); + $command->bindValue(':name',$name); + $command->bindValue(':description',$description); + $command->bindValue(':bizrule',$bizRule); + $command->bindValue(':data',serialize($data)); + $command->execute(); + return new CAuthItem($this,$name,$type,$description,$bizRule,$data); + } + + /** + * Removes the specified authorization item. + * @param string the name of the item to be removed + * @return boolean whether the item exists in the storage and has been removed + */ + public function removeAuthItem($name) + { + if($this->usingSqlite()) + { + $sql="DELETE FROM {$this->itemChildTable} WHERE parent=:name1 OR child=:name2"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name1',$name); + $command->bindValue(':name2',$name); + $command->execute(); + + $sql="DELETE FROM {$this->assignmentTable} WHERE itemname=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$name); + $command->execute(); + } + + $sql="DELETE FROM {$this->itemTable} WHERE name=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$name); + + return $command->execute()>0; + } + + /** + * Returns the authorization item with the specified name. + * @param string the name of the item + * @return CAuthItem the authorization item. Null if the item cannot be found. + */ + public function getAuthItem($name) + { + $sql="SELECT * FROM {$this->itemTable} WHERE name=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$name); + if(($row=$command->queryRow())!==false) + return new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],unserialize($row['data'])); + else + return null; + } + + /** + * Saves an authorization item to persistent storage. + * @param CAuthItem the item to be saved. + * @param string the old item name. If null, it means the item name is not changed. + */ + public function saveAuthItem($item,$oldName=null) + { + if($this->usingSqlite() && $oldName!==null && $item->getName()!==$oldName) + { + $sql="UPDATE {$this->itemChildTable} SET parent=:newName WHERE parent=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$oldName); + $command->bindValue(':newName',$item->getName()); + $command->execute(); + $sql="UPDATE {$this->itemChildTable} SET child=:newName WHERE child=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$oldName); + $command->bindValue(':newName',$item->getName()); + $command->execute(); + $sql="UPDATE {$this->assignmentTable} SET itemname=:newName WHERE itemname=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':name',$oldName); + $command->bindValue(':newName',$item->getName()); + $command->execute(); + } + + $sql="UPDATE {$this->itemTable} SET name=:newName, type=:type, description=:description, bizrule=:bizrule, data=:data WHERE name=:name"; + $command=$this->db->createCommand($sql); + $command->bindValue(':type',$item->getType()); + $command->bindValue(':name',$oldName===null?$item->getName():$oldName); + $command->bindValue(':newName',$item->getName()); + $command->bindValue(':description',$item->getDescription()); + $command->bindValue(':bizrule',$item->getBizRule()); + $command->bindValue(':data',serialize($item->getData())); + $command->execute(); + } + + /** + * Saves the authorization data to persistent storage. + */ + public function save() + { + } + + /** + * Removes all authorization data. + */ + public function clearAll() + { + $this->clearAuthAssignments(); + $this->db->createCommand("DELETE FROM {$this->itemChildTable}")->execute(); + $this->db->createCommand("DELETE FROM {$this->itemTable}")->execute(); + } + + /** + * Removes all authorization assignments. + */ + public function clearAuthAssignments() + { + $this->db->createCommand("DELETE FROM {$this->assignmentTable}")->execute(); + } + + /** + * Checks whether there is a loop in the authorization item hierarchy. + * @param string parent item name + * @param string the name of the child item that is to be added to the hierarchy + * @return boolean whether a loop exists + */ + protected function detectLoop($itemName,$childName) + { + if($childName===$itemName) + return true; + foreach($this->getItemChildren($childName) as $child) + { + if($this->detectLoop($itemName,$child->getName())) + return true; + } + return false; + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->db!==null) + return $this->db; + else if(($this->db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection) + return $this->db; + else + throw new CException(Yii::t('yii','CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$this->connectionID))); + } + + /** + * @return boolean whether the database is a SQLite database + */ + protected function usingSqlite() + { + return $this->_usingSqlite; + } +} diff --git a/web/framework-1.0.8/web/auth/CPhpAuthManager.php b/web/framework-1.0.8/web/auth/CPhpAuthManager.php new file mode 100644 index 0000000..efe6d92 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CPhpAuthManager.php @@ -0,0 +1,535 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPhpAuthManager represents an authorization manager that stores authorization information in terms of a PHP script file. + * + * The authorization data will be saved to and loaded from a file + * specified by {@link authFile}, which defaults to 'protected/data/auth.php'. + * + * CPhpAuthManager is mainly suitable for authorization data that is not too big + * (for example, the authorization data for a personal blog system). + * Use {@link CDbAuthManager} for more complex authorization data. + * + * @author Qiang Xue + * @version $Id: CPhpAuthManager.php 970 2009-04-30 02:59:02Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CPhpAuthManager extends CAuthManager +{ + /** + * @var string the path of the PHP script that contains the authorization data. + * If not set, it will be using 'protected/data/auth.php' as the data file. + * Make sure this file is writable by the Web server process if the authorization + * needs to be changed. + * @see loadFromFile + * @see saveToFile + */ + public $authFile; + + private $_items=array(); // itemName => item + private $_children=array(); // itemName, childName => child + private $_assignments=array(); // userId, itemName => assignment + + /** + * Initializes the application component. + * This method overrides parent implementation by loading the authorization data + * from PHP script. + */ + public function init() + { + parent::init(); + if($this->authFile===null) + $this->authFile=Yii::getPathOfAlias('application.data.auth').'.php'; + $this->load(); + } + + /** + * Performs access check for the specified user. + * @param string the name of the operation that need access check + * @param mixed the user ID. This should can be either an integer and a string representing + * the unique identifier of a user. See {@link IWebUser::getId}. + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user. + */ + public function checkAccess($itemName,$userId,$params=array()) + { + if(!empty($this->defaultRoles) && $this->checkDefaultRoles($itemName,$params)) + return true; + + // check directly assigned items + $names=array(); + foreach($this->getAuthAssignments($userId) as $assignment) + { + $item=$this->getAuthItem($assignment->getItemName()); + Yii::trace('Checking permission "'.($item ? $item->getName() : 'unknown').'"','system.web.auth.CPhpAuthManager'); + if($item!==null && $this->executeBizRule($assignment->getBizRule(),$params,$assignment->getData()) + && $this->executeBizRule($item->getBizRule(),$params,$item->getData())) + { + if($item->getName()===$itemName) + return true; + $names[]=$item->getName(); + } + } + + // check all descendant items + while($names!==array()) + { + $items=$this->getItemChildren($names); + $names=array(); + foreach($items as $item) + { + Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CPhpAuthManager'); + if($this->executeBizRule($item->getBizRule(),$params,$item->getData())) + { + if($item->getName()===$itemName) + return true; + $names[]=$item->getName(); + } + } + } + + return false; + } + + /** + * Checks the access based on the default roles as declared in {@link defaultRoles}. + * @param string the name of the operation that need access check + * @param array name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * @return boolean whether the operations can be performed by the user according to the default roles. + * @since 1.0.3 + */ + protected function checkDefaultRoles($itemName,$params) + { + foreach($this->defaultRoles as $role) + { + Yii::trace('Checking default role "'.$role.'"','system.web.auth.CPhpAuthManager'); + $item=$this->getAuthItem($role); + if($item!==null && $item->checkAccess($itemName,$params)) + return true; + } + return false; + } + + /** + * Adds an item as a child of another item. + * @param string the parent item name + * @param string the child item name + * @throws CException if either parent or child doesn't exist or if a loop has been detected. + */ + public function addItemChild($itemName,$childName) + { + if(!isset($this->_items[$childName],$this->_items[$itemName])) + throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{name}'=>$itemName))); + $child=$this->_items[$childName]; + $item=$this->_items[$itemName]; + $this->checkItemChildType($item->getType(),$child->getType()); + if($this->detectLoop($itemName,$childName)) + throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{parent}". A loop has been detected.', + array('{child}'=>$childName,'{parent}'=>$itemName))); + if(isset($this->_children[$itemName][$childName])) + throw new CException(Yii::t('yii','The item "{parent}" already has a child "{child}".', + array('{child}'=>$childName,'{parent}'=>$itemName))); + $this->_children[$itemName][$childName]=$this->_items[$childName]; + } + + /** + * Removes a child from its parent. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the removal is successful + */ + public function removeItemChild($itemName,$childName) + { + if(isset($this->_children[$itemName][$childName])) + { + unset($this->_children[$itemName][$childName]); + return true; + } + else + return false; + } + + /** + * Returns a value indicating whether a child exists within a parent. + * @param string the parent item name + * @param string the child item name + * @return boolean whether the child exists + */ + public function hasItemChild($itemName,$childName) + { + return isset($this->_children[$itemName][$childName]); + } + + /** + * Returns the children of the specified item. + * @param mixed the parent item name. This can be either a string or an array. + * The latter represents a list of item names (available since version 1.0.5). + * @return array all child items of the parent + */ + public function getItemChildren($names) + { + if(is_string($names)) + return isset($this->_children[$names]) ? $this->_children[$names] : array(); + + $children=array(); + foreach($names as $name) + { + if(isset($this->_children[$name])) + $children=array_merge($children,$this->_children[$name]); + } + return $children; + } + + /** + * Assigns an authorization item to a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @param string the business rule to be executed when {@link checkAccess} is called + * for this particular authorization item. + * @param mixed additional data associated with this assignment + * @return CAuthAssignment the authorization assignment information. + * @throws CException if the item does not exist or if the item has already been assigned to the user + */ + public function assign($itemName,$userId,$bizRule=null,$data=null) + { + if(!isset($this->_items[$itemName])) + throw new CException(Yii::t('yii','Unknown authorization item "{name}".',array('{name}'=>$itemName))); + else if(isset($this->_assignments[$userId][$itemName])) + throw new CException(Yii::t('yii','Authorization item "{item}" has already been assigned to user "{user}".', + array('{item}'=>$itemName,'{user}'=>$userId))); + else + return $this->_assignments[$userId][$itemName]=new CAuthAssignment($this,$itemName,$userId,$bizRule,$data); + } + + /** + * Revokes an authorization assignment from a user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether removal is successful + */ + public function revoke($itemName,$userId) + { + if(isset($this->_assignments[$userId][$itemName])) + { + unset($this->_assignments[$userId][$itemName]); + return true; + } + else + return false; + } + + /** + * Returns a value indicating whether the item has been assigned to the user. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return boolean whether the item has been assigned to the user. + */ + public function isAssigned($itemName,$userId) + { + return isset($this->_assignments[$userId][$itemName]); + } + + /** + * Returns the item assignment information. + * @param string the item name + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return CAuthAssignment the item assignment information. Null is returned if + * the item is not assigned to the user. + */ + public function getAuthAssignment($itemName,$userId) + { + return isset($this->_assignments[$userId][$itemName])?$this->_assignments[$userId][$itemName]:null; + } + + /** + * Returns the item assignments for the specified user. + * @param mixed the user ID (see {@link IWebUser::getId}) + * @return array the item assignment information for the user. An empty array will be + * returned if there is no item assigned to the user. + */ + public function getAuthAssignments($userId) + { + return isset($this->_assignments[$userId])?$this->_assignments[$userId]:array(); + } + + /** + * Returns the authorization items of the specific type and user. + * @param integer the item type (0: operation, 1: task, 2: role). Defaults to null, + * meaning returning all items regardless of their type. + * @param mixed the user ID. Defaults to null, meaning returning all items even if + * they are not assigned to a user. + * @return array the authorization items of the specific type. + */ + public function getAuthItems($type=null,$userId=null) + { + if($type===null && $userId===null) + return $this->_items; + $items=array(); + if($userId===null) + { + foreach($this->_items as $name=>$item) + { + if($item->getType()==$type) + $items[$name]=$item; + } + } + else if(isset($this->_assignments[$userId])) + { + foreach($this->_assignments[$userId] as $assignment) + { + $name=$assignment->getItemName(); + if(isset($this->_items[$name]) && ($type===null || $this->_items[$name]->getType()==$type)) + $items[$name]=$this->_items[$name]; + } + } + return $items; + } + + /** + * Creates an authorization item. + * An authorization item represents an action permission (e.g. creating a post). + * It has three types: operation, task and role. + * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * by lower level items. + * @param string the item name. This must be a unique identifier. + * @param integer the item type (0: operation, 1: task, 2: role). + * @param string description of the item + * @param string business rule associated with the item. This is a piece of + * PHP code that will be executed when {@link checkAccess} is called for the item. + * @param mixed additional data associated with the item. + * @return CAuthItem the authorization item + * @throws CException if an item with the same name already exists + */ + public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null) + { + if(isset($this->_items[$name])) + throw new CException(Yii::t('yii','Unable to add an item whose name is the same as an existing item.')); + return $this->_items[$name]=new CAuthItem($this,$name,$type,$description,$bizRule,$data); + } + + /** + * Removes the specified authorization item. + * @param string the name of the item to be removed + * @return boolean whether the item exists in the storage and has been removed + */ + public function removeAuthItem($name) + { + if(isset($this->_items[$name])) + { + foreach($this->_children as &$children) + unset($children[$name]); + foreach($this->_assignments as &$assignments) + unset($assignments[$name]); + unset($this->_items[$name]); + return true; + } + else + return false; + } + + /** + * Returns the authorization item with the specified name. + * @param string the name of the item + * @return CAuthItem the authorization item. Null if the item cannot be found. + */ + public function getAuthItem($name) + { + return isset($this->_items[$name])?$this->_items[$name]:null; + } + + /** + * Saves an authorization item to persistent storage. + * @param CAuthItem the item to be saved. + * @param string the old item name. If null, it means the item name is not changed. + */ + public function saveAuthItem($item,$oldName=null) + { + if(($newName=$item->getName())!==$oldName) // name changed + { + if(isset($this->_items[$newName])) + throw new CException(Yii::t('yii','Unable to change the item name. The name "{name}" is already used by another item.',array('{name}'=>$newName))); + if(isset($this->_items[$oldName]) && $this->_items[$oldName]===$item) + { + unset($this->_items[$oldName]); + $this->_items[$newName]=$item; + if(isset($this->_children[$oldName])) + { + $this->_children[$newName]=$this->_children[$oldName]; + unset($this->_children[$oldName]); + } + foreach($this->_children as &$children) + { + if(isset($children[$oldName])) + { + $children[$newName]=$children[$oldName]; + unset($children[$oldName]); + } + } + foreach($this->_assignments as &$assignments) + { + if(isset($assignments[$oldName])) + { + $assignments[$newName]=$assignments[$oldName]; + unset($assignments[$oldName]); + } + } + } + } + } + + /** + * Saves the changes to an authorization assignment. + * @param CAuthAssignment the assignment that has been changed. + */ + public function saveAuthAssignment($assignment) + { + } + + /** + * Saves authorization data into persistent storage. + * If any change is made to the authorization data, please make + * sure you call this method to save the changed data into persistent storage. + */ + public function save() + { + $items=array(); + foreach($this->_items as $name=>$item) + { + $items[$name]=array( + 'type'=>$item->getType(), + 'description'=>$item->getDescription(), + 'bizRule'=>$item->getBizRule(), + 'data'=>$item->getData(), + ); + if(isset($this->_children[$name])) + { + foreach($this->_children[$name] as $child) + $items[$name]['children'][]=$child->getName(); + } + } + + foreach($this->_assignments as $userId=>$assignments) + { + foreach($assignments as $name=>$assignment) + { + if(isset($items[$name])) + { + $items[$name]['assignments'][$userId]=array( + 'bizRule'=>$assignment->getBizRule(), + 'data'=>$assignment->getData(), + ); + } + } + } + + $this->saveToFile($items,$this->authFile); + } + + /** + * Loads authorization data. + */ + public function load() + { + $this->clearAll(); + + $items=$this->loadFromFile($this->authFile); + + foreach($items as $name=>$item) + $this->_items[$name]=new CAuthItem($this,$name,$item['type'],$item['description'],$item['bizRule'],$item['data']); + + foreach($items as $name=>$item) + { + if(isset($item['children'])) + { + foreach($item['children'] as $childName) + { + if(isset($this->_items[$childName])) + $this->_children[$name][$childName]=$this->_items[$childName]; + } + } + if(isset($item['assignments'])) + { + foreach($item['assignments'] as $userId=>$assignment) + { + $this->_assignments[$userId][$name]=new CAuthAssignment($this,$name,$userId,$assignment['bizRule'],$assignment['data']); + } + } + } + } + + /** + * Removes all authorization data. + */ + public function clearAll() + { + $this->clearAuthAssignments(); + $this->_children=array(); + $this->_items=array(); + } + + /** + * Removes all authorization assignments. + */ + public function clearAuthAssignments() + { + $this->_assignments=array(); + } + + /** + * Checks whether there is a loop in the authorization item hierarchy. + * @param string parent item name + * @param string the name of the child item that is to be added to the hierarchy + * @return boolean whether a loop exists + */ + protected function detectLoop($itemName,$childName) + { + if($childName===$itemName) + return true; + if(!isset($this->_children[$childName], $this->_items[$itemName])) + return false; + + foreach($this->_children[$childName] as $child) + { + if($this->detectLoop($itemName,$child->getName())) + return true; + } + return false; + } + + /** + * Loads the authorization data from a PHP script file. + * @param string the file path. + * @return array the authorization data + * @see saveToFile + */ + protected function loadFromFile($file) + { + if(is_file($file)) + return require($file); + else + return array(); + } + + /** + * Saves the authorization data to a PHP script file. + * @param array the authorization data + * @param string the file path. + * @see loadFromFile + */ + protected function saveToFile($data,$file) + { + file_put_contents($file," + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUserIdentity is a base class for representing identities that are authenticated based on a username and a password. + * + * Derived classes should implement {@link authenticate} with the actual + * authentication scheme (e.g. checking username and password against a DB table). + * + * By default, CUserIdentity assumes the {@link username} is a unique identifier + * and thus use it as the {@link id ID} of the identity. + * + * @author Qiang Xue + * @version $Id: CUserIdentity.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CUserIdentity extends CBaseUserIdentity +{ + /** + * @var string username + */ + public $username; + /** + * @var string password + */ + public $password; + + /** + * Constructor. + * @param string username + * @param string password + */ + public function __construct($username,$password) + { + $this->username=$username; + $this->password=$password; + } + + /** + * Authenticates a user based on {@link username} and {@link password}. + * Derived classes should override this method, or an exception will be thrown. + * This method is required by {@link IUserIdentity}. + * @return boolean whether authentication succeeds. + */ + public function authenticate() + { + throw new CException(Yii::t('yii','{class}::authenticate() must be implemented.',array('{class}'=>get_class($this)))); + } + + /** + * Returns the unique identifier for the identity. + * The default implementation simply returns {@link username}. + * This method is required by {@link IUserIdentity}. + * @return string the unique identifier for the identity. + */ + public function getId() + { + return $this->username; + } + + /** + * Returns the display name for the identity. + * The default implementation simply returns {@link username}. + * This method is required by {@link IUserIdentity}. + * @return string the display name for the identity. + */ + public function getName() + { + return $this->username; + } +} diff --git a/web/framework-1.0.8/web/auth/CWebUser.php b/web/framework-1.0.8/web/auth/CWebUser.php new file mode 100644 index 0000000..ab99ae0 --- /dev/null +++ b/web/framework-1.0.8/web/auth/CWebUser.php @@ -0,0 +1,583 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebUser represents the persistent state for a Web application user. + * + * CWebUser is used as an application component whose ID is 'user'. + * Therefore, at any place one can access the user state via + * Yii::app()->user. + * + * CWebUser should be used together with an {@link IUserIdentity identity} + * which implements the actual authentication algorithm. + * + * A typical authentication process using CWebUser is as follows: + *
      + *
    1. The user provides information needed for authentication.
    2. + *
    3. An {@link IUserIdentity identity instance} is created with the user-provided information.
    4. + *
    5. Call {@link IUserIdentity::authenticate} to check if the identity is valid.
    6. + *
    7. If valid, call {@link CWebUser::login} to login the user, and + * Redirect the user browser to {@link returnUrl}.
    8. + *
    9. If not valid, retrieve the error code or message from the identity + * instance and display it.
    10. + *
    + * + * The property {@link id} and {@link name} are both unique identifiers + * for the user. The former is mainly used internally (e.g. primary key), while + * the latter is for display purpose (e.g. username). is a unique identifier for a user that is persistent + * during the whole user session. It can be a username, or something else, + * depending on the implementation of the {@link IUserIdentity identity class}. + * + * Both {@link id} and {@link name} are persistent during the user session. + * Besides, an identity may have additional persistent data which can + * be accessed by calling {@link getState}. + * Note, when {@link allowAutoLogin cookie-based authentication} is enabled, + * all these persistent data will be stored in cookie. Therefore, do not + * store password or other sensitive data in the persistent storage. Instead, + * you should store them directly in session on the server side if needed. + * + * @author Qiang Xue + * @version $Id: CWebUser.php 1193 2009-06-29 19:04:30Z qiang.xue $ + * @package system.web.auth + * @since 1.0 + */ +class CWebUser extends CApplicationComponent implements IWebUser +{ + const FLASH_KEY_PREFIX='Yii.CWebUser.flash.'; + const FLASH_COUNTERS='Yii.CWebUser.flash.counters'; + const STATES_VAR='__states'; + + /** + * @var boolean whether to enable cookie-based login. Defaults to false. + */ + public $allowAutoLogin=false; + /** + * @var string the name for a guest user. Defaults to 'Guest'. + * This is used by {@link getName} when the current user is a guest (not authenticated). + */ + public $guestName='Guest'; + /** + * @var string|array the URL for login. If using array, the first element should be + * the route to the login action, and the rest name-value pairs are GET parameters + * to construct the login URL (e.g. array('site/login')). If this property is null, + * a 403 HTTP exception will be raised instead. + * @see CController::createUrl + */ + public $loginUrl=array('site/login'); + /** + * @var array the property values (in name-value pairs) used to initialize the identity cookie. + * Any property of {@link CHttpCookie} may be initialized. + * This property is effective only when {@link allowAutoLogin} is true. + * @since 1.0.5 + */ + public $identityCookie; + + private $_keyPrefix; + private $_access=array(); + + /** + * PHP magic method. + * This method is overriden so that persistent states can be accessed like properties. + * @param string property name + * @return mixed property value + * @since 1.0.3 + */ + public function __get($name) + { + if($this->hasState($name)) + return $this->getState($name); + else + return parent::__get($name); + } + + /** + * PHP magic method. + * This method is overriden so that persistent states can be set like properties. + * @param string property name + * @param mixed property value + * @since 1.0.3 + */ + public function __set($name,$value) + { + if($this->hasState($name)) + $this->setState($name,$value); + else + parent::__set($name,$value); + } + + /** + * PHP magic method. + * This method is overriden so that persistent states can also be checked for null value. + * @param string property name + * @since 1.0.3 + */ + public function __isset($name) + { + if($this->hasState($name)) + return $this->getState($name)!==null; + else + return parent::__isset($name); + } + + /** + * PHP magic method. + * This method is overriden so that persistent states can also be unset. + * @param string property name + * @throws CException if the property is read only. + * @since 1.0.3 + */ + public function __unset($name) + { + if($this->hasState($name)) + $this->setState($name,null); + else + parent::__unset($name); + } + + /** + * Initializes the application component. + * This method overrides the parent implementation by starting session, + * performing cookie-based authentication if enabled, and updating the flash variables. + */ + public function init() + { + parent::init(); + Yii::app()->getSession()->open(); + if($this->getIsGuest() && $this->allowAutoLogin) + $this->restoreFromCookie(); + $this->updateFlash(); + } + + /** + * Logs in a user. + * + * The user identity information will be saved in storage that is + * persistent during the user session. By default, the storage is simply + * the session storage. If the duration parameter is greater than 0, + * a cookie will be sent to prepare for cookie-based login in future. + * + * Note, you have to set {@link allowAutoLogin} to true + * if you want to allow user to be authenticated based on the cookie information. + * + * @param IUserIdentity the user identity (which should already be authenticated) + * @param integer number of seconds that the user can remain in logged-in status. Defaults to 0, meaning login till the user closes the browser. + * If greater than 0, cookie-based login will be used. In this case, {@link allowAutoLogin} + * must be set true, otherwise an exception will be thrown. + */ + public function login($identity,$duration=0) + { + $this->changeIdentity($identity->getId(),$identity->getName(),$identity->getPersistentStates()); + + if($duration>0) + { + if($this->allowAutoLogin) + $this->saveToCookie($duration); + else + throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.', + array('{class}'=>get_class($this)))); + } + } + + /** + * Logs out the current user. + * This will remove authentication-related session data. + * If the parameter is true, the whole session will be destroyed as well. + * @param boolean whether to destroy the whole session. Defaults to true. If false, + * then {@link clearStates} will be called, which removes only the data stored via {@link setState}. + * This parameter has been available since version 1.0.7. Before 1.0.7, the behavior + * is to destroy the whole session. + */ + public function logout($destroySession=true) + { + if($this->allowAutoLogin) + Yii::app()->getRequest()->getCookies()->remove($this->getStateKeyPrefix()); + if($destroySession) + Yii::app()->getSession()->destroy(); + else + $this->clearStates(); + } + + /** + * @return boolean whether the current application user is a guest. + */ + public function getIsGuest() + { + return $this->getState('__id')===null; + } + + /** + * @return mixed the unique identifier for the user. If null, it means the user is a guest. + */ + public function getId() + { + return $this->getState('__id'); + } + + /** + * @param mixed the unique identifier for the user. If null, it means the user is a guest. + */ + public function setId($value) + { + $this->setState('__id',$value); + } + + /** + * Returns the unique identifier for the user (e.g. username). + * This is the unique identifier that is mainly used for display purpose. + * @return string the user name. If the user is not logged in, this will be {@link guestName}. + */ + public function getName() + { + if(($name=$this->getState('__name'))!==null) + return $name; + else + return $this->guestName; + } + + /** + * Sets the unique identifier for the user (e.g. username). + * @param string the user name. + * @see getName + */ + public function setName($value) + { + $this->setState('__name',$value); + } + + /** + * Returns the URL that the user should be redirected to after successful login. + * This property is usually used by the login action. If the login is successful, + * the action should read this property and use it to redirect the user browser. + * @return string the URL that the user should be redirected to after login. Defaults to the application entry URL. + * @see loginRequired + */ + public function getReturnUrl() + { + return $this->getState('__returnUrl',Yii::app()->getRequest()->getScriptUrl()); + } + + /** + * @param string the URL that the user should be redirected to after login. + */ + public function setReturnUrl($value) + { + $this->setState('__returnUrl',$value); + } + + /** + * Redirects the user browser to the login page. + * Before the redirection, the current URL will be kept in {@link returnUrl} + * so that the user browser may be redirected back to the current page after successful login. + * Make sure you set {@link loginUrl} so that the user browser + * can be redirected to the specified login URL after calling this method. + * After calling this method, the current request processing will be terminated. + */ + public function loginRequired() + { + $app=Yii::app(); + $request=$app->getRequest(); + $this->setReturnUrl($request->getUrl()); + if(($url=$this->loginUrl)!==null) + { + if(is_array($url)) + { + $route=isset($url[0]) ? $url[0] : $app->defaultController; + $url=$app->createUrl($route,array_splice($url,1)); + } + + $request->redirect($url); + } + else + throw new CHttpException(403,Yii::t('yii','Login Required')); + } + + /** + * Populates the current user object with the information obtained from cookie. + * This method is used when automatic login ({@link allowAutoLogin}) is enabled. + * The user identity information is recovered from cookie. + * Sufficient security measures are used to prevent cookie data from being tampered. + * @see saveToCookie + */ + protected function restoreFromCookie() + { + $app=Yii::app(); + $cookie=$app->getRequest()->getCookies()->itemAt($this->getStateKeyPrefix()); + if($cookie && !empty($cookie->value) && ($data=$app->getSecurityManager()->validateData($cookie->value))!==false) + { + $data=unserialize($data); + if(isset($data[0],$data[1],$data[2])) + { + list($id,$name,$states)=$data; + $this->changeIdentity($id,$name,$states); + } + } + } + + /** + * Saves necessary user data into a cookie. + * This method is used when automatic login ({@link allowAutoLogin}) is enabled. + * This method saves user ID, username, other identity states and a validation key to cookie. + * These information are used to do authentication next time when user visits the application. + * @param integer number of seconds that the user can remain in logged-in status. Defaults to 0, meaning login till the user closes the browser. + * @see restoreFromCookie + */ + protected function saveToCookie($duration) + { + $app=Yii::app(); + $cookie=$this->createIdentityCookie($this->getStateKeyPrefix()); + $cookie->expire=time()+$duration; + $data=array( + $this->getId(), + $this->getName(), + $this->saveIdentityStates(), + ); + $cookie->value=$app->getSecurityManager()->hashData(serialize($data)); + $app->getRequest()->getCookies()->add($cookie->name,$cookie); + } + + /** + * Creates a cookie to store identity information. + * @param string the cookie name + * @return CHttpCookie the cookie used to store identity information + * @since 1.0.5 + */ + protected function createIdentityCookie($name) + { + $cookie=new CHttpCookie($name,''); + if(is_array($this->identityCookie)) + { + foreach($this->identityCookie as $name=>$value) + $cookie->$name=$value; + } + return $cookie; + } + + /** + * @return string a prefix for the name of the session variables storing user session data. + */ + protected function getStateKeyPrefix() + { + if($this->_keyPrefix!==null) + return $this->_keyPrefix; + else + return $this->_keyPrefix=md5('Yii.'.get_class($this).'.'.Yii::app()->getId()); + } + + /** + * Returns the value of a variable that is stored in user session. + * + * This function is designed to be used by CWebUser descendant classes + * who want to store additional user information in user session. + * A variable, if stored in user session using {@link setState} can be + * retrieved back using this function. + * + * @param string variable name + * @param mixed default value + * @return mixed the value of the variable. If it doesn't exist in the session, + * the provided default value will be returned + * @see setState + */ + public function getState($key,$defaultValue=null) + { + $key=$this->getStateKeyPrefix().$key; + return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue; + } + + /** + * Stores a variable in user session. + * + * This function is designed to be used by CWebUser descendant classes + * who want to store additional user information in user session. + * By storing a variable using this function, the variable may be retrieved + * back later using {@link getState}. The variable will be persistent + * across page requests during a user session. + * + * @param string variable name + * @param mixed variable value + * @param mixed default value. If $value===$defaultValue, the variable will be + * removed from the session + * @see getState + */ + public function setState($key,$value,$defaultValue=null) + { + // debuglog("setstate($key, $value)"); + $key=$this->getStateKeyPrefix().$key; + if($value===$defaultValue) + unset($_SESSION[$key]); + else + $_SESSION[$key]=$value; + } + + /** + * Returns a value indicating whether there is a state of the specified name. + * @param string state name + * @return boolean whether there is a state of the specified name. + * @since 1.0.3 + */ + public function hasState($key) + { + $key=$this->getStateKeyPrefix().$key; + return isset($_SESSION[$key]); + } + + /** + * Clears all user identity information from persistent storage. + * This will remove the data stored via {@link setState}. + */ + public function clearStates() + { + $keys=array_keys($_SESSION); + $prefix=$this->getStateKeyPrefix(); + $n=strlen($prefix); + foreach($keys as $key) + { + if(!strncmp($key,$prefix,$n)) + unset($_SESSION[$key]); + } + } + + /** + * Returns a flash message. + * A flash message is available only in the current and the next requests. + * @param string key identifying the flash message + * @param mixed value to be returned if the flash message is not available. + * @param boolean whether to delete this flash message after accessing it. + * Defaults to true. This parameter has been available since version 1.0.2. + * @return mixed the message message + */ + public function getFlash($key,$defaultValue=null,$delete=true) + { + $value=$this->getState(self::FLASH_KEY_PREFIX.$key,$defaultValue); + if($delete) + $this->setFlash($key,null); + return $value; + } + + /** + * Stores a flash message. + * A flash message is available only in the current and the next requests. + * @param string key identifying the flash message + * @param mixed flash message + * @param mixed if this value is the same as the flash message, the flash message + * will be removed. (Therefore, you can use setFlash('key',null) to remove a flash message.) + */ + public function setFlash($key,$value,$defaultValue=null) + { + $this->setState(self::FLASH_KEY_PREFIX.$key,$value,$defaultValue); + $counters=$this->getState(self::FLASH_COUNTERS,array()); + if($value===$defaultValue) + unset($counters[$key]); + else + $counters[$key]=0; + $this->setState(self::FLASH_COUNTERS,$counters,array()); + } + + /** + * @param string key identifying the flash message + * @return boolean whether the specified flash message exists + */ + public function hasFlash($key) + { + return $this->getFlash($key, null, false)!==null; + } + + /** + * Changes the current user with the specified identity information. + * This method is called by {@link login} and {@link restoreFromCookie} + * when the current user needs to be populated with the corresponding + * identity information. Derived classes may override this method + * by retrieving additional user-related information. Make sure the + * parent implementation is called first. + * @param mixed a unique identifier for the user + * @param string the display name for the user + * @param array identity states + */ + protected function changeIdentity($id,$name,$states) + { + $this->setId($id); + $this->setName($name); + $this->loadIdentityStates($states); + } + + /** + * Retrieves identity states from persistent storage and saves them as an array. + * @return array the identity states + */ + protected function saveIdentityStates() + { + $states=array(); + foreach($this->getState(self::STATES_VAR,array()) as $name=>$dummy) + $states[$name]=$this->getState($name); + return $states; + } + + /** + * Loads identity states from an array and saves them to persistent storage. + * @param array the identity states + */ + protected function loadIdentityStates($states) + { + $names=array(); + if(is_array($states)) + { + foreach($states as $name=>$value) + { + $this->setState($name,$value); + $names[$name]=true; + } + } + $this->setState(self::STATES_VAR,$names); + } + + /** + * Updates the internal counters for flash messages. + * This method is internally used by {@link CWebApplication} + * to maintain the availability of flash messages. + */ + protected function updateFlash() + { + $counters=$this->getState(self::FLASH_COUNTERS); + if(!is_array($counters)) + return; + foreach($counters as $key=>$count) + { + if($count) + { + unset($counters[$key]); + $this->setState(self::FLASH_KEY_PREFIX.$key,null); + } + else + $counters[$key]++; + } + $this->setState(self::FLASH_COUNTERS,$counters,array()); + } + + /** + * Performs access check for this user. + * @param string the name of the operation that need access check. + * @param array name-value pairs that would be passed to business rules associated + * with the tasks and roles assigned to the user. + * @param boolean whether to allow caching the result of access checki. + * This parameter has been available since version 1.0.5. When this parameter + * is true (default), if the access check of an operation was performed before, + * its result will be directly returned when calling this method to check the same operation. + * If this parameter is false, this method will always call {@link CAuthManager::checkAccess} + * to obtain the up-to-date access result. Note that this caching is effective + * only within the same request. + * @return boolean whether the operations can be performed by this user. + */ + public function checkAccess($operation,$params=array(),$allowCaching=true) + { + if($allowCaching && isset($this->_access[$operation])) + return $this->_access[$operation]; + else + return $this->_access[$operation]=Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params); + } +} diff --git a/web/framework-1.0.8/web/auth/schema.sql b/web/framework-1.0.8/web/auth/schema.sql new file mode 100644 index 0000000..40be828 --- /dev/null +++ b/web/framework-1.0.8/web/auth/schema.sql @@ -0,0 +1,42 @@ +/** + * Database schema required by CDbAuthManager. + * + * @author Qiang Xue + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @since 1.0 + */ + +drop table if exists AuthAssignment; +drop table if exists AuthItemChild; +drop table if exists AuthItem; + +create table AuthItem +( + name varchar(64) not null, + type integer not null, + description text, + bizrule text, + data text, + primary key (name) +); + +create table AuthItemChild +( + parent varchar(64) not null, + child varchar(64) not null, + primary key (parent,child), + foreign key (parent) references AuthItem (name) on delete cascade on update cascade, + foreign key (child) references AuthItem (name) on delete cascade on update cascade +); + +create table AuthAssignment +( + itemname varchar(64) not null, + userid varchar(64) not null, + bizrule text, + data text, + primary key (itemname,userid), + foreign key (itemname) references AuthItem (name) on delete cascade on update cascade +); diff --git a/web/framework-1.0.8/web/filters/CFilter.php b/web/framework-1.0.8/web/filters/CFilter.php new file mode 100644 index 0000000..5e13c51 --- /dev/null +++ b/web/framework-1.0.8/web/filters/CFilter.php @@ -0,0 +1,64 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFilter is the base class for all filters. + * + * A filter can be applied before and after an action is executed. + * It can modify the context that the action is to run or decorate the result that the + * action generates. + * + * Override {@link preFilter()} to specify the filtering logic that should be applied + * before the action, and {@link postFilter()} for filtering logic after the action. + * + * @author Qiang Xue + * @version $Id: CFilter.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.filters + * @since 1.0 + */ +class CFilter extends CComponent implements IFilter +{ + /** + * Performs the filtering. + * The default implementation is to invoke {@link preFilter} + * and {@link postFilter} which are meant to be overridden + * child classes. If a child class needs to override this method, + * make sure it calls $filterChain->run() + * if the action should be executed. + * @param CFilterChain the filter chain that the filter is on. + */ + public function filter($filterChain) + { + if($this->preFilter($filterChain)) + { + $filterChain->run(); + $this->postFilter($filterChain); + } + } + + /** + * Performs the pre-action filtering. + * @param CFilterChain the filter chain that the filter is on. + * @return boolean whether the filtering process should continue and the action + * should be executed. + */ + protected function preFilter($filterChain) + { + return true; + } + + /** + * Performs the post-action filtering. + * @param CFilterChain the filter chain that the filter is on. + */ + protected function postFilter($filterChain) + { + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/filters/CFilterChain.php b/web/framework-1.0.8/web/filters/CFilterChain.php new file mode 100644 index 0000000..57e3fb4 --- /dev/null +++ b/web/framework-1.0.8/web/filters/CFilterChain.php @@ -0,0 +1,131 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CFilterChain represents a list of filters being applied to an action. + * + * CFilterChain executes the filter list by {@link run()}. + * + * @author Qiang Xue + * @version $Id: CFilterChain.php 861 2009-03-21 03:45:21Z qiang.xue $ + * @package system.web.filters + * @since 1.0 + */ +class CFilterChain extends CList +{ + /** + * @var CController the controller who executes the action. + */ + public $controller; + /** + * @var CAction the action being filtered by this chain. + */ + public $action; + /** + * @var integer the index of the filter that is to be executed when calling {@link run()}. + */ + public $filterIndex=0; + + + /** + * Constructor. + * @param CController the controller who executes the action. + * @param CAction the action being filtered by this chain. + */ + public function __construct($controller,$action) + { + $this->controller=$controller; + $this->action=$action; + } + + /** + * CFilterChain factory method. + * This method creates a CFilterChain instance. + * @param CController the controller who executes the action. + * @param CAction the action being filtered by this chain. + * @param array list of filters to be applied to the action. + */ + public static function create($controller,$action,$filters) + { + $chain=new CFilterChain($controller,$action); + + $actionID=$action->getId(); + foreach($filters as $filter) + { + if(is_string($filter)) // filterName [+|- action1 action2] + { + if(($pos=strpos($filter,'+'))!==false || ($pos=strpos($filter,'-'))!==false) + { + $matched=preg_match("/\b{$actionID}\b/i",substr($filter,$pos+1))>0; + if(($filter[$pos]==='+')===$matched) + $chain->add(CInlineFilter::create($controller,trim(substr($filter,0,$pos)))); + } + else + $chain->add(CInlineFilter::create($controller,$filter)); + } + else if(is_array($filter)) // array('path.to.class [+|- action1, action2]','param1'=>'value1',...) + { + if(!isset($filter[0])) + throw new CException(Yii::t('yii','The first element in a filter configuration must be the filter class.')); + $filterClass=$filter[0]; + unset($filter[0]); + if(($pos=strpos($filterClass,'+'))!==false || ($pos=strpos($filterClass,'-'))!==false) + { + $matched=preg_match("/\b{$actionID}\b/i",substr($filterClass,$pos+1))>0; + if(($filterClass[$pos]==='+')===$matched) + $filterClass=trim(substr($filterClass,0,$pos)); + else + continue; + } + $filter['class']=$filterClass; + $chain->add(Yii::createComponent($filter)); + } + else + $chain->add($filter); + } + return $chain; + } + + /** + * Inserts an item at the specified position. + * This method overrides the parent implementation by adding + * additional check for the item to be added. In particular, + * only objects implementing {@link IFilter} can be added to the list. + * @param integer the specified position. + * @param mixed new item + * @throws CException If the index specified exceeds the bound or the list is read-only, or the item is not an {@link IFilter} instance. + */ + public function insertAt($index,$item) + { + if($item instanceof IFilter) + parent::insertAt($index,$item); + else + throw new CException(Yii::t('yii','CFilterChain can only take objects implementing the IFilter interface.')); + } + + /** + * Executes the filter indexed at {@link filterIndex}. + * After this method is called, {@link filterIndex} will be automatically incremented by one. + * This method is usually invoked in filters so that the filtering process + * can continue and the action can be executed. + */ + public function run() + { + if($this->offsetExists($this->filterIndex)) + { + $filter=$this->itemAt($this->filterIndex++); + Yii::trace('Running filter '.($filter instanceof CInlineFilter ? get_class($this->controller).'.filter'.$filter->name.'()':get_class($filter).'.filter()'),'system.web.filters.CFilterChain'); + $filter->filter($this); + } + else + $this->controller->runAction($this->action); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/filters/CInlineFilter.php b/web/framework-1.0.8/web/filters/CInlineFilter.php new file mode 100644 index 0000000..947da1b --- /dev/null +++ b/web/framework-1.0.8/web/filters/CInlineFilter.php @@ -0,0 +1,61 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CInlineFilter represents a filter defined as a controller method. + * + * CInlineFilter executes the 'filterXYZ($action)' method defined + * in the controller, where the name 'XYZ' can be retrieved from the {@link name} property. + * + * @author Qiang Xue + * @version $Id: CInlineFilter.php 511 2009-01-12 15:16:40Z qiang.xue $ + * @package system.web.filters + * @since 1.0 + */ +class CInlineFilter extends CFilter +{ + /** + * @var string name of the filter. It stands for 'XYZ' in the filter method name 'filterXYZ'. + */ + public $name; + + /** + * Creates an inline filter instance. + * The creation is based on a string describing the inline method name + * and action names that the filter shall or shall not apply to. + * @param CController the controller who hosts the filter methods + * @param string the filter name + * @return CInlineFilter the created instance + * @throws CException if the filter method does not exist + */ + public static function create($controller,$filterName) + { + if(method_exists($controller,'filter'.$filterName)) + { + $filter=new CInlineFilter; + $filter->name=$filterName; + return $filter; + } + else + throw new CException(Yii::t('yii','Filter "{filter}" is invalid. Controller "{class}" does have the filter method "filter{filter}".', + array('{filter}'=>$filterName, '{class}'=>get_class($controller)))); + } + + /** + * Performs the filtering. + * This method calls the filter method defined in the controller class. + * @param CFilterChain the filter chain that the filter is on. + */ + public function filter($filterChain) + { + $method='filter'.$this->name; + $filterChain->controller->$method($filterChain); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/helpers/CGoogleApi.php b/web/framework-1.0.8/web/helpers/CGoogleApi.php new file mode 100644 index 0000000..7ed50a0 --- /dev/null +++ b/web/framework-1.0.8/web/helpers/CGoogleApi.php @@ -0,0 +1,72 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CGoogleApi provides helper methods to easily access {@link http://code.google.com/apis/ajax/ Google AJAX APIs}. + * + * @author Qiang Xue + * @version $Id: CGoogleApi.php 757 2009-02-26 21:13:19Z qiang.xue $ + * @package system.web.helpers + * @since 1.0.3 + */ +class CGoogleApi +{ + const BOOTSTRAP_URL='http://www.google.com/jsapi'; + + /** + * Renders the jsapi script file. + * @param string the API key. Null if you do not have a key. + * @return string the script tag that loads Google jsapi. + */ + public static function init($apiKey=null) + { + if($apiKey===null) + return CHtml::scriptFile(self::BOOTSTRAP_URL); + else + return CHtml::scriptFile(self::BOOTSTRAP_URL.'?key='.$apiKey); + } + + /** + * Loads the specified API module. + * Note that you should call {@link init} first. + * @param string the module name + * @param string the module version + * @param array additional js options that are to be passed to the load() function. + * @return string the js code for loading the module. You can use {@link CHtml::script()} + * to enclose it in a script tag. + */ + public static function load($name,$version='1',$options=array()) + { + if(empty($options)) + return "google.load(\"{$name}\",\"{$version}\");"; + else + return "google.load(\"{$name}\",\"{$version}\",".CJavaScript::encode($options).");"; + } + + /** + * Registers the specified API module. + * This is similar to {@link load} except that it registers the loading code + * with {@link CClientScript} instead of returning it. + * This method also registers the jsapi script needed by the loading call. + * @param string the module name + * @param string the module version + * @param array additional js options that are to be passed to the load() function. + * @param string the API key. Null if you do not have a key. + */ + public static function register($name,$version='1',$options=array(),$apiKey=null) + { + $cs=Yii::app()->getClientScript(); + $url=$apiKey===null?self::BOOTSTRAP_URL:self::BOOTSTRAP_URL.'?key='.$apiKey; + $cs->registerScriptFile($url); + + $js=self::load($name,$version,$options); + $cs->registerScript($name,$js,CClientScript::POS_HEAD); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/helpers/CHtml.php b/web/framework-1.0.8/web/helpers/CHtml.php new file mode 100644 index 0000000..8b5266e --- /dev/null +++ b/web/framework-1.0.8/web/helpers/CHtml.php @@ -0,0 +1,1801 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CHtml is a static class that provides a collection of helper methods for creating HTML views. + * + * @author Qiang Xue + * @version $Id: CHtml.php 1306 2009-08-07 17:24:31Z qiang.xue $ + * @package system.web.helpers + * @since 1.0 + */ +class CHtml +{ + const ID_PREFIX='yt'; + /** + * @var string the CSS class for displaying error summaries (see {@link errorSummary}). + */ + public static $errorSummaryCss='errorSummary'; + /** + * @var string the CSS class for displaying error messages (see {@link error}). + */ + public static $errorMessageCss='errorMessage'; + /** + * @var string the CSS class for highlighting error inputs. Form inputs will be appended + * with this CSS class if they have input errors. + */ + public static $errorCss='error'; + /** + * @var string the CSS class for required labels. Defaults to 'required'. + * @see label + */ + public static $requiredCss='required'; + /** + * @var string the HTML code to be prepended to the required label. + * @see label + */ + public static $beforeRequiredLabel=''; + /** + * @var string the HTML code to be appended to the required label. + * @see label + */ + public static $afterRequiredLabel=' *'; + /** + * @var string the scenario used to determine whether a model attribute is required. + * @see activeLabelEx + */ + public static $scenario=''; + /** + * @var integer the counter for generating automatic input field names. + * @since 1.0.4 + */ + public static $count=0; + + /** + * Encodes special characters into HTML entities. + * The {@link CApplication::charset application charset} will be used for encoding. + * @param string data to be encoded + * @return string the encoded data + * @see http://www.php.net/manual/en/function.htmlspecialchars.php + */ + public static function encode($text) + { + $res = @htmlspecialchars($text, ENT_QUOTES,Yii::app()->charset); + if(!$res) + $res = htmlentities($text); //,Yii::app()->charset); + + return $res; + } + + /** + * Encodes special characters in an array of strings into HTML entities. + * Both the array keys and values will be encoded if needed. + * If a value is an array, this method will also encode it recursively. + * The {@link CApplication::charset application charset} will be used for encoding. + * @param array data to be encoded + * @return array the encoded data + * @see http://www.php.net/manual/en/function.htmlspecialchars.php + * @since 1.0.4 + */ + public static function encodeArray($data) + { + $d=array(); + foreach($data as $key=>$value) + { + if(is_string($key)) + $key=htmlspecialchars($key,ENT_QUOTES,Yii::app()->charset); + if(is_string($value)) + $value=htmlspecialchars($value,ENT_QUOTES,Yii::app()->charset); + else if(is_array($value)) + $value=self::encodeArray($value); + $d[$key]=$value; + } + return $d; + } + + /** + * Generates an HTML element. + * @param string the tag name + * @param array the element attributes. The values will be HTML-encoded using {@link encode()}. + * Since version 1.0.5, if an 'encode' attribute is given and its value is false, + * the rest of the attribute values will NOT be HTML-encoded. + * @param mixed the content to be enclosed between open and close element tags. It will not be HTML-encoded. + * If false, it means there is no body content. + * @param boolean whether to generate the close tag. + * @return string the generated HTML element tag + */ + public static function tag($tag,$htmlOptions=array(),$content=false,$closeTag=true) + { + $html='<' . $tag . self::renderAttributes($htmlOptions); + if($content===false) + return $closeTag ? $html.' />' : $html.'>'; + else + return $closeTag ? $html.'>'.$content.'' : $html.'>'.$content; + } + + /** + * Generates an open HTML element. + * @param string the tag name + * @param array the element attributes. The values will be HTML-encoded using {@link encode()}. + * Since version 1.0.5, if an 'encode' attribute is given and its value is false, + * the rest of the attribute values will NOT be HTML-encoded. + * @return string the generated HTML element tag + */ + public static function openTag($tag,$htmlOptions=array()) + { + return '<' . $tag . self::renderAttributes($htmlOptions) . '>'; + } + + /** + * Generates a close HTML element. + * @param string the tag name + * @return string the generated HTML element tag + */ + public static function closeTag($tag) + { + return ''; + } + + /** + * Encloses the given string within a CDATA tag. + * @param string the string to be enclosed + * @return string the CDATA tag with the enclosed content. + */ + public static function cdata($text) + { + return ''; + } + + /** + * Generates a meta tag that can be inserted in the head section of HTML page. + * @param string content attribute of the meta tag + * @param string name attribute of the meta tag. If null, the attribute will not be generated + * @param string http-equiv attribute of the meta tag. If null, the attribute will not be generated + * @param array other options in name-value pairs (e.g. 'scheme', 'lang') + * @return string the generated meta tag + * @since 1.0.1 + */ + public static function metaTag($content,$name=null,$httpEquiv=null,$options=array()) + { + $options['content']=$content; + if($name!==null) + $options['name']=$name; + if($httpEquiv!==null) + $options['http-equiv']=$httpEquiv; + return self::tag('meta',$options); + } + + /** + * Generates a link tag that can be inserted in the head section of HTML page. + * Do not confuse this method with {@link link()}. The latter generates a hyperlink. + * @param string rel attribute of the link tag. If null, the attribute will not be generated. + * @param string type attribute of the link tag. If null, the attribute will not be generated. + * @param string href attribute of the link tag. If null, the attribute will not be generated. + * @param string media attribute of the link tag. If null, the attribute will not be generated. + * @param array other options in name-value pairs + * @return string the generated link tag + * @since 1.0.1 + */ + public static function linkTag($relation=null,$type=null,$href=null,$media=null,$options=array()) + { + if($relation!==null) + $options['rel']=$relation; + if($type!==null) + $options['type']=$type; + if($href!==null) + $options['href']=$href; + if($media!==null) + $options['media']=$media; + return self::tag('link',$options); + } + + /** + * Encloses the given CSS content with a CSS tag. + * @param string the CSS content + * @param string the media that this CSS should apply to. + * @return string the CSS properly enclosed + */ + public static function css($text,$media='') + { + if($media!=='') + $media=' media="'.$media.'"'; + return ""; + } + + /** + * Links to the specified CSS file. + * @param string the CSS URL + * @param string the media that this CSS should apply to. + * @return string the CSS link. + */ + public static function cssFile($url,$media='') + { + if($media!=='') + $media=' media="'.$media.'"'; + return ''; + } + + /** + * Encloses the given JavaScript within a script tag. + * @param string the JavaScript to be enclosed + * @return string the enclosed JavaScript + */ + public static function script($text) + { + return ""; + } + + /** + * Includes a JavaScript file. + * @param string URL for the JavaScript file + * @return string the JavaScript file tag + */ + public static function scriptFile($url) + { + return ''; + } + + /** + * Generates an opening form tag. + * This is a shortcut to {@link beginForm}. + * @param mixed the form action URL (see {@link normalizeUrl} for details about this parameter.) + * @param string form method (e.g. post, get) + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated form tag. + */ + public static function form($action='',$method='post',$htmlOptions=array()) + { + return self::beginForm($action,$method,$htmlOptions); + } + + /** + * Generates an opening form tag. + * Note, only the open tag is generated. A close tag should be placed manually + * at the end of the form. + * @param mixed the form action URL (see {@link normalizeUrl} for details about this parameter.) + * @param string form method (e.g. post, get) + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated form tag. + * @since 1.0.4 + * @see endForm + */ + public static function beginForm($action='',$method='post',$htmlOptions=array()) + { + $htmlOptions['action']=self::normalizeUrl($action); + $htmlOptions['method']=$method; + $form=self::tag('form',$htmlOptions,false,false); + $request=Yii::app()->request; + if($request->enableCsrfValidation) + { + $token=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false)); + $form.="\n".$token; + } + return $form; + } + + /** + * Generates a closing form tag. + * @return string the generated tag + * @since 1.0.4 + * @see beginForm + */ + public static function endForm() + { + return ''; + } + + /** + * Generates a stateful form tag. + * A stateful form tag is similar to {@link form} except that it renders an additional + * hidden field for storing persistent page states. You should use this method to generate + * a form tag if you want to access persistent page states when the form is submitted. + * @param mixed the form action URL (see {@link normalizeUrl} for details about this parameter.) + * @param string form method (e.g. post, get) + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated form tag. + */ + public static function statefulForm($action='',$method='post',$htmlOptions=array()) + { + return self::form($action,$method,$htmlOptions)."\n". + self::tag('div',array('style'=>'visibility:hidden'),self::pageStateField('')); + } + + /** + * Generates a hidden field for storing persistent page states. + * This method is internally used by {@link statefulForm}. + * @param string the persistent page states in serialized format + * @return string the generated hidden field + */ + public static function pageStateField($value) + { + return ''; + } + + /** + * Generates a hyperlink tag. + * @param string link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag. + * @param mixed a URL or an action route that can be used to create a URL. + * See {@link normalizeUrl} for more details about how to specify this parameter. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated hyperlink + * @see normalizeUrl + * @see clientChange + */ + public static function link($text,$url='#',$htmlOptions=array()) + { + if($url!=='') + $htmlOptions['href']=self::normalizeUrl($url); + self::clientChange('click',$htmlOptions); + return self::tag('a',$htmlOptions,$text); + } + + /** + * Generates a mailto link. + * @param string link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag. + * @param string email address. If this is empty, the first parameter (link body) will be treated as the email address. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated mailto link + * @see clientChange + * @since 1.0.1 + */ + public static function mailto($text,$email='',$htmlOptions=array()) + { + if($email==='') + $email=$text; + return self::link($text,'mailto:'.$email,$htmlOptions); + } + + /** + * Generates an image tag. + * @param string the image URL + * @param string the alternative text display + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated image tag + */ + public static function image($src,$alt='',$htmlOptions=array()) + { + $htmlOptions['src']=$src; + $htmlOptions['alt']=$alt; + return self::tag('img',$htmlOptions); + } + + /** + * Generates a button. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + */ + public static function button($label='button',$htmlOptions=array()) + { + if(!isset($htmlOptions['name'])) + $htmlOptions['name']=self::ID_PREFIX.self::$count++; + if(!isset($htmlOptions['type'])) + $htmlOptions['type']='button'; + if(!isset($htmlOptions['value'])) + $htmlOptions['value']=$label; + self::clientChange('click',$htmlOptions); + return self::tag('input',$htmlOptions); + } + + /** + * Generates a button using HTML button tag. + * This method is similar to {@link button} except that it generates a 'button' + * tag instead of 'input' tag. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + * @since 1.0.8 + */ + public static function htmlButton($label='button',$htmlOptions=array()) + { + if(!isset($htmlOptions['name'])) + $htmlOptions['name']=self::ID_PREFIX.self::$count++; + if(!isset($htmlOptions['type'])) + $htmlOptions['type']='button'; + if(!isset($htmlOptions['value'])) + $htmlOptions['value']=$label; + self::clientChange('click',$htmlOptions); + return self::tag('button',$htmlOptions); + } + + /** + * Generates a submit button. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + */ + public static function submitButton($label='submit',$htmlOptions=array()) + { + $htmlOptions['type']='submit'; + return self::button($label,$htmlOptions); + } + + /** + * Generates a reset button. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + */ + public static function resetButton($label='reset',$htmlOptions=array()) + { + $htmlOptions['type']='reset'; + return self::button($label,$htmlOptions); + } + + /** + * Generates an image submit button. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + */ + public static function imageButton($src,$htmlOptions=array()) + { + $htmlOptions['src']=$src; + $htmlOptions['type']='image'; + return self::button('submit',$htmlOptions); + } + + /** + * Generates a link submit button. + * @param string the button label + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button tag + * @see clientChange + */ + public static function linkButton($label='submit',$htmlOptions=array()) + { + if(!isset($htmlOptions['submit'])) + $htmlOptions['submit']=isset($htmlOptions['href']) ? $htmlOptions['href'] : ''; + return self::link($label,'#',$htmlOptions); + } + + /** + * Generates a label tag. + * @param string label text. Note, you should HTML-encode the text if needed. + * @param string the ID of the HTML element that this label is associated with + * @param array additional HTML attributes. + * Starting from version 1.0.2, the following HTML option is recognized: + *
    +	 * 
  • required: if this is set and is true, the label will be styled + * with CSS class 'required' (customizable with CHtml::$requiredCss), + * and be decorated with {@link CHtml::beforeRequiredLabel} and + * {@link CHtml::afterRequiredLabel}.
  • + *
    + * @return string the generated label tag + */ + public static function label($label,$for,$htmlOptions=array()) + { + $htmlOptions['for']=$for; + if(isset($htmlOptions['required'])) + { + if($htmlOptions['required']) + { + if(isset($htmlOptions['class'])) + $htmlOptions['class'].=' '.self::$requiredCss; + else + $htmlOptions['class']=self::$requiredCss; + $label=self::$beforeRequiredLabel.$label.self::$afterRequiredLabel; + } + unset($htmlOptions['required']); + } + return self::tag('label',$htmlOptions,$label); + } + + /** + * Generates a text field input. + * @param string the input name + * @param string the input value + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + */ + public static function textField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('text',$name,$value,$htmlOptions); + } + + /** + * Generates a hidden input. + * @param string the input name + * @param string the input value + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated input field + * @see inputField + */ + public static function hiddenField($name,$value='',$htmlOptions=array()) + { + return self::inputField('hidden',$name,$value,$htmlOptions); + } + + /** + * Generates a password field input. + * @param string the input name + * @param string the input value + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + */ + public static function passwordField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('password',$name,$value,$htmlOptions); + } + + /** + * Generates a file input. + * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'. + * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name] (see + * PHP documentation). + * @param string the input name + * @param string the input value + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated input field + * @see inputField + */ + public static function fileField($name,$value='',$htmlOptions=array()) + { + return self::inputField('file',$name,$value,$htmlOptions); + } + + /** + * Generates a text area input. + * @param string the input name + * @param string the input value + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated text area + * @see clientChange + * @see inputField + */ + public static function textArea($name,$value='',$htmlOptions=array()) + { + $htmlOptions['name']=$name; + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=self::getIdByName($name); + self::clientChange('change',$htmlOptions); + return self::tag('textarea',$htmlOptions,self::encode($value)); + } + + /** + * Generates a radio button. + * @param string the input name + * @param boolean whether the check box is checked + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated radio button + * @see clientChange + * @see inputField + */ + public static function radioButton($name,$checked=false,$htmlOptions=array()) + { + if($checked) + $htmlOptions['checked']='checked'; + else + unset($htmlOptions['checked']); + $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1; + self::clientChange('click',$htmlOptions); + return self::inputField('radio',$name,$value,$htmlOptions); + } + + /** + * Generates a check box. + * @param string the input name + * @param boolean whether the check box is checked + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated check box + * @see clientChange + * @see inputField + */ + public static function checkBox($name,$checked=false,$htmlOptions=array()) + { + if($checked) + $htmlOptions['checked']='checked'; + else + unset($htmlOptions['checked']); + $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1; + self::clientChange('click',$htmlOptions); + return self::inputField('checkbox',$name,$value,$htmlOptions); + } + + /** + * Generates a drop down list. + * @param string the input name + * @param string the selected value + * @param array data for generating the list options (value=>display). + * You may use {@link listData} to generate this data. + * Please refer to {@link listOptions} on how this data is used to generate the list options. + * Note, the values and labels will be automatically HTML-encoded by this method. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are recognized. See {@link clientChange} and {@link tag} for more details. + * In addition, the following options are also supported specifically for dropdown list: + *
      + *
    • prompt: string, specifies the prompt text shown as the first list option. Its value is empty.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty.
    • + *
    • options: array, specifies additional attributes for each OPTION tag. + * The array keys must be the option values, and the array values are the extra + * OPTION tag attributes in the name-value pairs. For example, + *
      +	 *     array(
      +	 *         'value1'=>array('disabled'=>true, 'label'=>'value 1'),
      +	 *         'value2'=>array('label'=>'value 2'),
      +	 *     );
      +	 * 
      + * This option has been available since version 1.0.3. + *
    • + *
    + * @return string the generated drop down list + * @see clientChange + * @see inputField + * @see listData + */ + public static function dropDownList($name,$select,$data,$htmlOptions=array()) + { + $htmlOptions['name']=$name; + + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=self::getIdByName($name); + self::clientChange('change',$htmlOptions); + $options="\n".self::listOptions($select,$data,$htmlOptions); + return self::tag('select',$htmlOptions,$options); + } + + /** + * Generates a list box. + * @param string the input name + * @param mixed the selected value(s). This can be either a string for single selection or an array for multiple selections. + * @param array data for generating the list options (value=>display) + * You may use {@link listData} to generate this data. + * Please refer to {@link listOptions} on how this data is used to generate the list options. + * Note, the values and labels will be automatically HTML-encoded by this method. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized. See {@link clientChange} and {@link tag} for more details. + * In addition, the following options are also supported specifically for list box: + *
      + *
    • prompt: string, specifies the prompt text shown as the first list option. Its value is empty.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty.
    • + *
    • options: array, specifies additional attributes for each OPTION tag. + * The array keys must be the option values, and the array values are the extra + * OPTION tag attributes in the name-value pairs. For example, + *
      +	 *     array(
      +	 *         'value1'=>array('disabled'=>true, 'label'=>'value 1'),
      +	 *         'value2'=>array('label'=>'value 2'),
      +	 *     );
      +	 * 
      + * This option has been available since version 1.0.3. + *
    • + *
    + * @return string the generated list box + * @see clientChange + * @see inputField + * @see listData + */ + public static function listBox($name,$select,$data,$htmlOptions=array()) + { + if(!isset($htmlOptions['size'])) + $htmlOptions['size']=4; + if(isset($htmlOptions['multiple'])) + { + if(substr($name,-2)!=='[]') + $name.='[]'; + } + return self::dropDownList($name,$select,$data,$htmlOptions); + } + + /** + * Generates a check box list. + * A check box list allows multiple selection, like {@link listBox}. + * As a result, the corresponding POST value is an array. + * @param string name of the check box list. You can use this name to retrieve + * the selected value(s) once the form is submitted. + * @param mixed selection of the check boxes. This can be either a string + * for single selection or an array for multiple selections. + * @param array value-label pairs used to generate the check box list. + * Note, the values will be automatically HTML-encoded, while the labels will not. + * @param array addtional HTML options. The options will be applied to + * each checkbox input. The following special options are recognized: + *
      + *
    • template: string, specifies how each checkbox is rendered. Defaults + * to "{input} {label}", where "{input}" will be replaced by the generated + * check box input tag while "{label}" be replaced by the corresponding check box label.
    • + *
    • separator: string, specifies the string that separates the generated check boxes.
    • + *
    • checkAll: string, specifies the label for the "check all" checkbox. + * If this option is specified, a 'check all' checkbox will be displayed. Clicking on + * this checkbox will cause all checkboxes checked or unchecked. This option has been + * available since version 1.0.4.
    • + *
    • checkAllLast: boolean, specifies whether the 'check all' checkbox should be + * displayed at the end of the checkbox list. If this option is not set (default) + * or is false, the 'check all' checkbox will be displayed at the beginning of + * the checkbox list. This option has been available since version 1.0.4.
    • + *
    + * @return string the generated check box list + */ + public static function checkBoxList($name,$select,$data,$htmlOptions=array()) + { + $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + unset($htmlOptions['template'],$htmlOptions['separator']); + + if(substr($name,-2)!=='[]') + $name.='[]'; + + if(isset($htmlOptions['checkAll'])) + { + $checkAllLabel=$htmlOptions['checkAll']; + $checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast']; + } + unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']); + + $items=array(); + $baseID=self::getIdByName($name); + $id=0; + $checkAll=true; + foreach($data as $value=>$label) + { + $checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select); + $checkAll=$checkAll && $checked; + $htmlOptions['value']=$value; + $htmlOptions['id']=$baseID.'_'.$id++; + $option=self::checkBox($name,$checked,$htmlOptions); + $label=self::label($label,$htmlOptions['id']); + $items[]=strtr($template,array('{input}'=>$option,'{label}'=>$label)); + } + + if(isset($checkAllLabel)) + { + $htmlOptions['value']=1; + $htmlOptions['id']=$id=$baseID.'_all'; + $option=self::checkBox($id,$checkAll,$htmlOptions); + $label=self::label($checkAllLabel,$id); + $item=strtr($template,array('{input}'=>$option,'{label}'=>$label)); + if($checkAllLast) + $items[]=$item; + else + array_unshift($items,$item); + $name=strtr($name,array('['=>'\\[',']'=>'\\]')); + $js=<<getClientScript(); + $cs->registerCoreScript('jquery'); + $cs->registerScript($id,$js); + } + + return implode($separator,$items); + } + + /** + * Generates a radio button list. + * A radio button list is like a {@link checkBoxList check box list}, except that + * it only allows single selection. + * @param string name of the radio button list. You can use this name to retrieve + * the selected value(s) once the form is submitted. + * @param mixed selection of the radio buttons. This can be either a string + * for single selection or an array for multiple selections. + * @param array value-label pairs used to generate the radio button list. + * Note, the values will be automatically HTML-encoded, while the labels will not. + * @param array addtional HTML options. The options will be applied to + * each checkbox input. The following special options are recognized: + *
      + *
    • template: string, specifies how each checkbox is rendered. Defaults + * to "{input} {label}", where "{input}" will be replaced by the generated + * radio button input tag while "{label}" be replaced by the corresponding radio button label.
    • + *
    • separator: string, specifies the string that separates the generated radio buttons.
    • + *
    + * @return string the generated radio button list + */ + public static function radioButtonList($name,$select,$data,$htmlOptions=array()) + { + $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + unset($htmlOptions['template'],$htmlOptions['separator']); + + $items=array(); + $baseID=self::getIdByName($name); + $id=0; + foreach($data as $value=>$label) + { + $checked=!strcmp($value,$select); + $htmlOptions['value']=$value; + $htmlOptions['id']=$baseID.'_'.$id++; + $option=self::radioButton($name,$checked,$htmlOptions); + $label=self::label($label,$htmlOptions['id']); + $items[]=strtr($template,array('{input}'=>$option,'{label}'=>$label)); + } + return implode($separator,$items); + } + + /** + * Generates a link that can initiate AJAX requests. + * @param string the link body (it will NOT be HTML-encoded.) + * @param mixed the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. + * @param array AJAX options (see {@link ajax}) + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated link + * @see normalizeUrl + * @see ajax + */ + public static function ajaxLink($text,$url,$ajaxOptions=array(),$htmlOptions=array()) + { + if(!isset($htmlOptions['href'])) + $htmlOptions['href']='#'; + $ajaxOptions['url']=$url; + $htmlOptions['ajax']=$ajaxOptions; + self::clientChange('click',$htmlOptions); + return self::tag('a',$htmlOptions,$text); + } + + /** + * Generates a push button that can initiate AJAX requests. + * @param string the button label + * @param mixed the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. + * @param array AJAX options (see {@link ajax}) + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button + */ + public static function ajaxButton($label,$url,$ajaxOptions=array(),$htmlOptions=array()) + { + $ajaxOptions['url']=$url; + $htmlOptions['ajax']=$ajaxOptions; + return self::button($label,$htmlOptions); + } + + /** + * Generates a push button that can submit the current form in POST method. + * @param string the button label + * @param mixed the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details. + * @param array AJAX options (see {@link ajax}) + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated button + */ + public static function ajaxSubmitButton($label,$url,$ajaxOptions=array(),$htmlOptions=array()) + { + $ajaxOptions['type']='POST'; + return self::ajaxButton($label,$url,$ajaxOptions,$htmlOptions); + } + + /** + * Generates the JavaScript that initiates an AJAX request. + * @param array AJAX options. The valid options are specified in the jQuery ajax documentation. + * The following special options are added for convenience: + *
      + *
    • update: string, specifies the selector whose HTML content should be replaced + * by the AJAX request result.
    • + *
    • replace: string, specifies the selector whose target should be replaced + * by the AJAX request result.
    • + *
    + * Note, if you specify the 'success' option, the above options will be ignored. + * @return string the generated JavaScript + * @see http://docs.jquery.com/Ajax/jQuery.ajax#options + */ + public static function ajax($options) + { + Yii::app()->getClientScript()->registerCoreScript('jquery'); + if(!isset($options['url'])) + $options['url']='js:location.href'; + else + $options['url']=self::normalizeUrl($options['url']); + if(!isset($options['cache'])) + $options['cache']=false; + if(!isset($options['data']) && isset($options['type'])) + $options['data']='js:jQuery(this).parents("form").serialize()'; + foreach(array('beforeSend','complete','error','success') as $name) + { + if(isset($options[$name]) && strpos($options[$name],'js:')!==0) + $options[$name]='js:'.$options[$name]; + } + if(isset($options['update'])) + { + if(!isset($options['success'])) + $options['success']='js:function(html){jQuery("'.$options['update'].'").html(html)}'; + unset($options['update']); + } + if(isset($options['replace'])) + { + if(!isset($options['success'])) + $options['success']='js:function(html){jQuery("'.$options['replace'].'").replaceWith(html)}'; + unset($options['replace']); + } + return 'jQuery.ajax('.CJavaScript::encode($options).');'; + } + + /** + * Generates the URL for the published assets. + * @param string the path of the asset to be published + * @param boolean whether the published directory should be named as the hashed basename. + * If false, the name will be the hashed dirname of the path being published. + * Defaults to false. Set true if the path being published is shared among + * different extensions. + * @return string the asset URL + */ + public static function asset($path,$hashByName=false) + { + return Yii::app()->getAssetManager()->publish($path,$hashByName); + } + + /** + * Generates a URL if the input specifies the route to a controller action. + * @param mixed the URL to be normalized. If a string, the URL is returned back; + * if an array, it is considered as a route to a controller action and will + * be used to generate a URL using {@link CController::createUrl}; if the URL is empty, + * the currently requested URL is returned. + * @param string the URL + */ + public static function normalizeUrl($url) + { + if(is_array($url)) + { + if(isset($url[0])) + { + if(($c=Yii::app()->getController())!==null) + $url=$c->createUrl($url[0],array_splice($url,1)); + else + $url=Yii::app()->createUrl($url[0],array_splice($url,1)); + } + else + $url=''; + } + return $url==='' ? Yii::app()->getRequest()->getUrl() : $url; + } + + /** + * Generates an input HTML tag. + * This method generates an input HTML tag based on the given input name and value. + * @param string the input type (e.g. 'text', 'radio') + * @param string the input name + * @param string the input value + * @param array additional HTML attributes for the HTML tag (see {@link tag}). + * @return string the generated input tag + */ + protected static function inputField($type,$name,$value,$htmlOptions) + { + $htmlOptions['type']=$type; + $htmlOptions['value']=$value; + $htmlOptions['name']=$name; + +// if($type != 'checkbox') +// $htmlOptions['class']=$htmlOptions['class'].' tweetnews-input'; + + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=self::getIdByName($name); + else if($htmlOptions['id']===false) + unset($htmlOptions['id']); + return self::tag('input',$htmlOptions); + } + + /** + * Generates a label tag for a model attribute. + * The label text is the attribute label and the label is associated with + * the input for the attribute (see {@link CModel::getAttributeLabel}. + * If the attribute has input error, the label's CSS class will be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. A special option named + * 'label' is recognized since version 1.0.4. If this option is specified, it will be used + * as the label. Otherwise, the label will be determined + * by calling {@link CModel::getAttributeLabel}. + * @return string the generated label tag + */ + public static function activeLabel($model,$attribute,$htmlOptions=array()) + { + if(isset($htmlOptions['for'])) + { + $for=$htmlOptions['for']; + unset($htmlOptions['for']); + } + else + $for=self::getIdByName(self::resolveName($model,$attribute)); + if(isset($htmlOptions['label'])) + { + $label=$htmlOptions['label']; + unset($htmlOptions['label']); + } + else + $label=$model->getAttributeLabel($attribute); + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + return self::label($label,$for,$htmlOptions); + } + + /** + * Generates a label tag for a model attribute. + * This is an enhanced version of {@link activeLabel}. It will render additional + * CSS class and mark when the attribute is required. + * In particular, it calls {@link CModel::isAttributeRequired} to determine + * if the attribute is required under the scenario {@link CHtml::scenario}. + * If so, it will add a CSS class {@link CHtml::requiredCss} to the label, + * and decorate the label with {@link CHtml::beforeRequiredLabel} and + * {@link CHtml::afterRequiredLabel}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. + * @return string the generated label tag + * @since 1.0.2 + */ + public static function activeLabelEx($model,$attribute,$htmlOptions=array()) + { + $realAttribute=$attribute; + self::resolveName($model,$attribute); // strip off square brackets if any + $htmlOptions['required']=$model->isAttributeRequired($attribute,self::$scenario); + return self::activeLabel($model,$realAttribute,$htmlOptions); + } + + /** + * Generates a text field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + */ + public static function activeTextField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('text',$model,$attribute,$htmlOptions); + } + + /** + * Generates a hidden input for a model attribute. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. + * @return string the generated input field + * @see activeInputField + */ + public static function activeHiddenField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + return self::activeInputField('hidden',$model,$attribute,$htmlOptions); + } + + /** + * Generates a password field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + */ + public static function activePasswordField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('password',$model,$attribute,$htmlOptions); + } + + /** + * Generates a text area input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated text area + * @see clientChange + */ + public static function activeTextArea($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + return self::tag('textarea',$htmlOptions,self::encode($model->$attribute)); + } + + /** + * Generates a file input for a model attribute. + * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'. + * After the form is submitted, the uploaded file information can be obtained via $_FILES (see + * PHP documentation). + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes (see {@link tag}). + * @return string the generated input field + * @see activeInputField + */ + public static function activeFileField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + // add a hidden field so that if a model only has a file field, we can + // still use isset($_POST[$modelClass]) to detect if the input is submitted + return self::hiddenField($htmlOptions['name'],'',array('id'=>self::ID_PREFIX.$htmlOptions['id'])) + . self::activeInputField('file',$model,$attribute,$htmlOptions); + } + + /** + * Generates a radio button for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated radio button + * @see clientChange + * @see activeInputField + */ + public static function activeRadioButton($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + if(!isset($htmlOptions['value'])) + $htmlOptions['value']=1; + if($model->$attribute) + $htmlOptions['checked']='checked'; + self::clientChange('click',$htmlOptions); + // add a hidden field so that if the radio button is not selected, it still submits a value + return self::hiddenField($htmlOptions['name'],$htmlOptions['value']?0:-1,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) + . self::activeInputField('radio',$model,$attribute,$htmlOptions); + } + + /** + * Generates a check box for a model attribute. + * The attribute is assumed to take either true or false value. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * Since version 1.0.2, a special option named 'uncheckValue' is available that can be used to specify + * the value returned when the checkbox is not checked. By default, this value is '0'. + * @return string the generated check box + * @see clientChange + * @see activeInputField + */ + public static function activeCheckBox($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + if(!isset($htmlOptions['value'])) + $htmlOptions['value']=1; + if($model->$attribute) + $htmlOptions['checked']='checked'; + self::clientChange('click',$htmlOptions); + + if(isset($htmlOptions['uncheckValue'])) + { + $uncheck=$htmlOptions['uncheckValue']; + unset($htmlOptions['uncheckValue']); + } + else + $uncheck='0'; + + return self::hiddenField($htmlOptions['name'],$uncheck,array('id'=>self::ID_PREFIX.$htmlOptions['id'])) + . self::activeInputField('checkbox',$model,$attribute,$htmlOptions); + } + + /** + * Generates a drop down list for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array data for generating the list options (value=>display) + * You may use {@link listData} to generate this data. + * Please refer to {@link listOptions} on how this data is used to generate the list options. + * Note, the values and labels will be automatically HTML-encoded by this method. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are recognized. See {@link clientChange} and {@link tag} for more details. + * In addition, the following options are also supported: + *
      + *
    • prompt: string, specifies the prompt text shown as the first list option. Its value is empty.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty.
    • + *
    • options: array, specifies additional attributes for each OPTION tag. + * The array keys must be the option values, and the array values are the extra + * OPTION tag attributes in the name-value pairs. For example, + *
      +	 *     array(
      +	 *         'value1'=>array('disabled'=>true, 'label'=>'value 1'),
      +	 *         'value2'=>array('label'=>'value 2'),
      +	 *     );
      +	 * 
      + * This option has been available since version 1.0.3. + *
    • + *
    + * @return string the generated drop down list + * @see clientChange + * @see listData + */ + public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + $selection=$model->$attribute; + $options="\n".self::listOptions($selection,$data,$htmlOptions); + self::clientChange('change',$htmlOptions); + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + if(isset($htmlOptions['multiple'])) + { + if(substr($htmlOptions['name'],-2)!=='[]') + $htmlOptions['name'].='[]'; + } + + return self::tag('select',$htmlOptions,$options); + } + + /** + * Generates a list box for a model attribute. + * The model attribute value is used as the selection. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array data for generating the list options (value=>display) + * You may use {@link listData} to generate this data. + * Please refer to {@link listOptions} on how this data is used to generate the list options. + * Note, the values and labels will be automatically HTML-encoded by this method. + * @param array additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are recognized. See {@link clientChange} and {@link tag} for more details. + * In addition, the following options are also supported: + *
      + *
    • prompt: string, specifies the prompt text shown as the first list option. Its value is empty.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty.
    • + *
    • options: array, specifies additional attributes for each OPTION tag. + * The array keys must be the option values, and the array values are the extra + * OPTION tag attributes in the name-value pairs. For example, + *
      +	 *     array(
      +	 *         'value1'=>array('disabled'=>true, 'label'=>'value 1'),
      +	 *         'value2'=>array('label'=>'value 2'),
      +	 *     );
      +	 * 
      + * This option has been available since version 1.0.3. + *
    • + *
    + * @return string the generated list box + * @see clientChange + * @see listData + */ + public static function activeListBox($model,$attribute,$data,$htmlOptions=array()) + { + if(!isset($htmlOptions['size'])) + $htmlOptions['size']=4; + return self::activeDropDownList($model,$attribute,$data,$htmlOptions); + } + + /** + * Generates a check box list for a model attribute. + * The model attribute value is used as the selection. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * Note that a check box list allows multiple selection, like {@link listBox}. + * As a result, the corresponding POST value is an array. In case no selection + * is made, the corresponding POST value is an empty string. + * @param CModel the data model + * @param string the attribute + * @param array value-label pairs used to generate the check box list. + * Note, the values will be automatically HTML-encoded, while the labels will not. + * @param array addtional HTML options. The options will be applied to + * each checkbox input. The following special options are recognized: + *
      + *
    • template: string, specifies how each checkbox is rendered. Defaults + * to "{input} {label}", where "{input}" will be replaced by the generated + * check box input tag while "{label}" be replaced by the corresponding check box label.
    • + *
    • separator: string, specifies the string that separates the generated check boxes.
    • + *
    • checkAll: string, specifies the label for the "check all" checkbox. + * If this option is specified, a 'check all' checkbox will be displayed. Clicking on + * this checkbox will cause all checkboxes checked or unchecked. This option has been + * available since version 1.0.4.
    • + *
    • checkAllLast: boolean, specifies whether the 'check all' checkbox should be + * displayed at the end of the checkbox list. If this option is not set (default) + * or is false, the 'check all' checkbox will be displayed at the beginning of + * the checkbox list. This option has been available since version 1.0.4.
    • + *
    • encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true. + * This option has been available since version 1.0.5.
    • + *
    + * @return string the generated check box list + * @see checkBoxList + */ + public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + $selection=$model->$attribute; + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + $name=$htmlOptions['name']; + unset($htmlOptions['name']); + + return self::hiddenField($name,'',array('id'=>self::ID_PREFIX.$htmlOptions['id'])) + . self::checkBoxList($name,$selection,$data,$htmlOptions); + } + + /** + * Generates a radio button list for a model attribute. + * The model attribute value is used as the selection. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel the data model + * @param string the attribute + * @param array value-label pairs used to generate the radio button list. + * Note, the values will be automatically HTML-encoded, while the labels will not. + * @param array addtional HTML options. The options will be applied to + * each checkbox input. The following special options are recognized: + *
      + *
    • template: string, specifies how each checkbox is rendered. Defaults + * to "{input} {label}", where "{input}" will be replaced by the generated + * radio button input tag while "{label}" be replaced by the corresponding radio button label.
    • + *
    • separator: string, specifies the string that separates the generated radio buttons.
    • + *
    • encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true. + * This option has been available since version 1.0.5.
    • + *
    + * @return string the generated radio button list + * @see radioButtonList + */ + public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + $selection=$model->$attribute; + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + $name=$htmlOptions['name']; + unset($htmlOptions['name']); + + return self::hiddenField($name,'',array('id'=>self::ID_PREFIX.$htmlOptions['id'])) + . self::radioButtonList($name,$selection,$data,$htmlOptions); + } + + /** + * Returns the element ID that is used by methods such as {@link activeTextField}. + * This method has been deprecated since version 1.0.5. Please use {@link activeId} instead. + * @param CModel the data model + * @param string the attribute + * @return string the element ID for the active field corresponding to the specified model and attribute. + * @deprecated 1.0.5 + */ + public static function getActiveId($model,$attribute) + { + return self::activeId($model,$attribute); + } + + /** + * Displays a summary of validation errors for one or several models. + * @param mixed the models whose input errors are to be displayed. This can be either + * a single model or an array of models. + * @param string a piece of HTML code that appears in front of the errors + * @param string a piece of HTML code that appears at the end of the errors + * @param array additional HTML attributes to be rendered in the container div tag. + * This parameter has been available since version 1.0.7. + * @return string the error summary. Empty if no errors are found. + * @see CModel::getErrors + * @see errorSummaryCss + */ + public static function errorSummary($model,$header=null,$footer=null,$htmlOptions=array()) + { + $content=''; + if(!is_array($model)) + $model=array($model); + foreach($model as $m) + { + foreach($m->getErrors() as $errors) + { + foreach($errors as $error) + { + if($error!='') + $content.="
  • $error
  • \n"; + } + } + } + if($content!=='') + { + if($header===null) + $header='

    '.Yii::t('yii','Please fix the following input errors:').'

    '; + if(!isset($htmlOptions['class'])) + $htmlOptions['class']=self::$errorSummaryCss; + return self::tag('div',$htmlOptions,$header."\n
      \n$content
    ".$footer); + } + else + return ''; + } + + /** + * Displays the first validation error for a model attribute. + * @param CModel the data model + * @param string the attribute name + * @param array additional HTML attributes to be rendered in the container div tag. + * This parameter has been available since version 1.0.7. + * @return string the error display. Empty if no errors are found. + * @see CModel::getErrors + * @see errorMessageCss + */ + public static function error($model,$attribute,$htmlOptions=array()) + { + $error=$model->getError($attribute); + if($error!='') + { + if(!isset($htmlOptions['class'])) + $htmlOptions['class']=self::$errorMessageCss; + return self::tag('div',$htmlOptions,$error); + } + else + return ''; + } + + /** + * Generates the data suitable for {@link dropDownList} and {@link listBox}. + * Note, this method does not HTML-encode the generated data. You may call {@link encodeArray} to + * encode it if needed. + * Please refer to the {@link value} method on how to specify value field, text field and group field. + * @param array a list of model objects. Starting from version 1.0.3, this parameter + * can also be an array of associative arrays (e.g. results of {@link CDbCommand::queryAll}). + * @param string the attribute name for list option values + * @param string the attribute name for list option texts + * @param string the attribute name for list option group names. If empty, no group will be generated. + * @return array the list data that can be used in {@link dropDownList} and {@link listBox} + */ + public static function listData($models,$valueField,$textField,$groupField='') + { + $listData=array(); + if($groupField==='') + { + foreach($models as $model) + { + $value=self::value($model,$valueField); + $text=self::value($model,$textField); + $listData[$value]=$text; + } + } + else + { + foreach($models as $model) + { + $group=self::value($model,$groupField); + $value=self::value($model,$valueField); + $text=self::value($model,$textField); + $listData[$group][$value]=$text; + } + } + return $listData; + } + + /** + * Evaluates the value of the specified attribute for the given model. + * The attribute name can be given in a dot syntax. For example, if the attribute + * is "author.firstName", this method will return the value of "$model->author->firstName". + * A default value (passed as the last parameter) will be returned if the attribute does + * not exist or is broken in the middle (e.g. $model->author is null). + * The model can be either an object or an array. If the latter, the attribute is treated + * as a key of the array. For the example of "author.firstName", if would mean the array value + * "$model['author']['firstName']". + * @param mixed the model. This can be either an object or an array. + * @param string the attribute name (use dot to concatenate multiple attributes) + * @param mixed the default value to return when the attribute does not exist + * @return mixed the attribute value + * @since 1.0.5 + */ + public static function value($model,$attribute,$defaultValue=null) + { + foreach(explode('.',$attribute) as $name) + { + if(is_object($model)) + $model=$model->$name; + else if(is_array($model) && isset($model[$name])) + $model=$model[$name]; + else + return $defaultValue; + } + return $model; + } + + /** + * Generates a valid HTML ID based the name. + * @return string the ID generated based on name. + */ + public static function getIdByName($name) + { + return str_replace(array('[]', '][', '[', ']'), array('', '_', '_', ''), $name); + } + + /** + * Generates input field ID for a model attribute. + * @param CModel the data model + * @param string the attribute + * @return string the generated input field ID + * @since 1.0.1 + */ + public static function activeId($model,$attribute) + { + return self::getIdByName(self::activeName($model,$attribute)); + } + + /** + * Generates input field name for a model attribute. + * @param CModel the data model + * @param string the attribute + * @return string the generated input field name + * @since 1.0.1 + */ + public static function activeName($model,$attribute) + { + $a=$attribute; // because the attribute name may be changed by resolveName + return self::resolveName($model,$a); + } + + /** + * Generates an input HTML tag for a model attribute. + * This method generates an input HTML tag based on the given data model and attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * This enables highlighting the incorrect input. + * @param string the input type (e.g. 'text', 'radio') + * @param CModel the data model + * @param string the attribute + * @param array additional HTML attributes for the HTML tag + * @return string the generated input tag + */ + protected static function activeInputField($type,$model,$attribute,$htmlOptions) + { + $htmlOptions['type']=$type; + if($type==='file') + unset($htmlOptions['value']); + else if(!isset($htmlOptions['value'])) + $htmlOptions['value']=$model->$attribute; + if($model->hasErrors($attribute)) + self::addErrorCss($htmlOptions); + return self::tag('input',$htmlOptions); + } + + /** + * Generates the list options. + * @param mixed the selected value(s). This can be either a string for single selection or an array for multiple selections. + * @param array the option data (see {@link listData}) + * @param array additional HTML attributes. The following two special attributes are recognized: + *
      + *
    • encode: boolean, specifies whether to encode the values. Defaults to true. This option has been available since version 1.0.5.
    • + *
    • prompt: string, specifies the prompt text shown as the first list option. Its value is empty.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty.
    • + *
    • options: array, specifies additional attributes for each OPTION tag. + * The array keys must be the option values, and the array values are the extra + * OPTION tag attributes in the name-value pairs. For example, + *
      +	 *     array(
      +	 *         'value1'=>array('disabled'=>true, 'label'=>'value 1'),
      +	 *         'value2'=>array('label'=>'value 2'),
      +	 *     );
      +	 * 
      + * This option has been available since version 1.0.3. + *
    • + *
    + * @return string the generated list options + */ + public static function listOptions($selection,$listData,&$htmlOptions) + { + $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode']; + $content=''; + if(isset($htmlOptions['prompt'])) + { + $content.='"; + unset($htmlOptions['prompt']); + } + if(isset($htmlOptions['empty'])) + { + $content.='"; + unset($htmlOptions['empty']); + } + + if(isset($htmlOptions['options'])) + { + $options=$htmlOptions['options']; + unset($htmlOptions['options']); + } + else + $options=array(); + + foreach($listData as $key=>$value) + { + if(is_array($value)) + { + $content.='\n"; + $dummy=array(); + $content.=self::listOptions($selection,$value,$dummy); + $content.=''."\n"; + } + else + { + $attributes=array('value'=>(string)$key, 'encode'=>!$raw); + if(!is_array($selection) && !strcmp($key,$selection) || + is_array($selection) && in_array($key,$selection)) + $attributes['selected']='selected'; + if(isset($options[$key])) + $attributes=array_merge($attributes,$options[$key]); + $content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value)); + } + } + return $content; + } + + /** + * Generates the JavaScript with the specified client changes. + * @param string event name (without 'on') + * @param array HTML attributes which may contain the following special attributes + * specifying the client change behaviors: + *
      + *
    • submit: string, specifies the URL that the button should submit to. If empty, the current requested URL will be used.
    • + *
    • params: array, name-value pairs that should be submitted together with the form. This is only used when 'submit' option is specified.
    • + *
    • csrf: boolean, whether a CSRF token should be submitted when {@link CHttpRequest::enableCsrfValidation} is true. Defaults to false. + * This option has been available since version 1.0.7. You may want to set this to be true if there is no enclosing + * form around this element. This option is meaningful only when 'submit' option is set.
    • + *
    • return: boolean, the return value of the javascript. Defaults to false, meaning that the execution of + * javascript would not cause the default behavior of the event. This option has been available since version 1.0.2.
    • + *
    • confirm: string, specifies the message that should show in a pop-up confirmation dialog.
    • + *
    • ajax: array, specifies the AJAX options (see {@link ajax}).
    • + *
    + */ + protected static function clientChange($event,&$htmlOptions) + { + if(!isset($htmlOptions['submit']) && !isset($htmlOptions['confirm']) && !isset($htmlOptions['ajax'])) + return; + + if(isset($htmlOptions['return']) && $htmlOptions['return']) + $return='return true'; + else + $return='return false'; + + if(isset($htmlOptions['on'.$event])) + { + $handler=trim($htmlOptions['on'.$event],';').';'; + unset($htmlOptions['on'.$event]); + } + else + $handler=''; + + if(isset($htmlOptions['id'])) + $id=$htmlOptions['id']; + else + $id=$htmlOptions['id']=isset($htmlOptions['name'])?$htmlOptions['name']:self::ID_PREFIX.self::$count++; + + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('jquery'); + + if(isset($htmlOptions['submit'])) + { + $cs->registerCoreScript('yii'); + $request=Yii::app()->getRequest(); + if($request->enableCsrfValidation && isset($htmlOptions['csrf']) && $htmlOptions['csrf']) + $htmlOptions['params'][$request->csrfTokenName]=$request->getCsrfToken(); + if(isset($htmlOptions['params'])) + $params=CJavaScript::encode($htmlOptions['params']); + else + $params='{}'; + if($htmlOptions['submit']!=='') + $url=CJavaScript::quote(self::normalizeUrl($htmlOptions['submit'])); + else + $url=''; + $handler.="jQuery.yii.submitForm(this,'$url',$params);{$return};"; + } + + if(isset($htmlOptions['ajax'])) + $handler.=self::ajax($htmlOptions['ajax'])."{$return};"; + + if(isset($htmlOptions['confirm'])) + { + $confirm='confirm(\''.CJavaScript::quote($htmlOptions['confirm']).'\')'; + if($handler!=='') + $handler="if($confirm) {".$handler."} else return false;"; + else + $handler="return $confirm;"; + } + + $cs->registerScript('Yii.CHtml.#'.$id,"jQuery('#$id').$event(function(){{$handler}});"); + unset($htmlOptions['params'],$htmlOptions['submit'],$htmlOptions['ajax'],$htmlOptions['confirm'],$htmlOptions['return'],$htmlOptions['csrf']); + } + + /** + * Generates input name and ID for a model attribute. + * This method will update the HTML options by setting appropriate 'name' and 'id' attributes. + * @param CModel the data model + * @param string the attribute + * @param array the HTML options + */ + protected static function resolveNameID($model,&$attribute,&$htmlOptions) + { + if(!isset($htmlOptions['name'])) + $htmlOptions['name']=self::resolveName($model,$attribute); + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=self::getIdByName($htmlOptions['name']); + } + + /** + * Generates input name for a model attribute. + * @param CModel the data model + * @param string the attribute + * @return string the input name + * @since 1.0.2 + */ + protected static function resolveName($model,&$attribute) + { + if(($pos=strpos($attribute,'['))!==false) + { + $sub=substr($attribute,$pos); + $attribute=substr($attribute,0,$pos); + return get_class($model).$sub.'['.$attribute.']'; + } + else + return get_class($model).'['.$attribute.']'; + } + + /** + * Appends {@link errorCss} to the 'class' attribute. + * @param array HTML options to be modified + */ + protected static function addErrorCss(&$htmlOptions) + { + if(isset($htmlOptions['class'])) + $htmlOptions['class'].=' '.self::$errorCss; + else + $htmlOptions['class']=self::$errorCss; + } + + /** + * Renders the HTML tag attributes. + * @param array attributes to be rendered + * @return string the rendering result + * @since 1.0.5 + */ + protected static function renderAttributes($htmlOptions) + { + if($htmlOptions===array()) + return ''; + $html=''; + $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode']; + unset($htmlOptions['encode']); + if($raw) + { + foreach($htmlOptions as $name=>$value) + $html .= ' ' . $name . '="' . $value . '"'; + } + else + { + foreach($htmlOptions as $name=>$value) + $html .= ' ' . $name . '="' . self::encode($value) . '"'; + } + return $html; + } +} diff --git a/web/framework-1.0.8/web/helpers/CJSON.php b/web/framework-1.0.8/web/helpers/CJSON.php new file mode 100644 index 0000000..b646ef2 --- /dev/null +++ b/web/framework-1.0.8/web/helpers/CJSON.php @@ -0,0 +1,698 @@ + +* @author Matt Knapp +* @author Brett Stimmerman +* @copyright 2005 Michal Migurski +* @license http://www.opensource.org/licenses/bsd-license.php +* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 +*/ + +/** + * CJSON converts PHP data to and from JSON format. + * + * @author Michal Migurski + * @author Matt Knapp + * @author Brett Stimmerman + * @version $Id: CJSON.php 569 2009-01-24 01:34:35Z qiang.xue $ + * @package system.web.helpers + * @since 1.0 + */ +class CJSON +{ + /** + * Marker constant for JSON::decode(), used to flag stack state + */ + const JSON_SLICE = 1; + + /** + * Marker constant for JSON::decode(), used to flag stack state + */ + const JSON_IN_STR = 2; + + /** + * Marker constant for JSON::decode(), used to flag stack state + */ + const JSON_IN_ARR = 4; + + /** + * Marker constant for JSON::decode(), used to flag stack state + */ + const JSON_IN_OBJ = 8; + + /** + * Marker constant for JSON::decode(), used to flag stack state + */ + const JSON_IN_CMT = 16; + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return string JSON string representation of input var + */ + public static function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + if (($enc=strtoupper(Yii::app()->charset))!=='UTF-8') + $var=iconv($enc, 'UTF-8', $var); + + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c+1})); + $c+=1; + $utf16 = self::utf8ToUTF16BE($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c+1}), + ord($var{$c+2})); + $c+=2; + $utf16 = self::utf8ToUTF16BE($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c+1}), + ord($var{$c+2}), + ord($var{$c+3})); + $c+=3; + $utf16 = self::utf8ToUTF16BE($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c+1}), + ord($var{$c+2}), + ord($var{$c+3}), + ord($var{$c+4})); + $c+=4; + $utf16 = self::utf8ToUTF16BE($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c+1}), + ord($var{$c+2}), + ord($var{$c+3}), + ord($var{$c+4}), + ord($var{$c+5})); + $c+=5; + $utf16 = self::utf8ToUTF16BE($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + return '{' . + join(',', array_map(array('CJSON', 'nameValue'), + array_keys($var), + array_values($var))) + . '}'; + } + + // treat it like a regular array + return '[' . join(',', array_map(array('CJSON', 'encode'), $var)) . ']'; + + case 'object': + if ($var instanceof Traversable) + { + $vars = array(); + foreach ($var as $k=>$v) + $vars[$k] = $v; + } + else + $vars = get_object_vars($var); + return '{' . + join(',', array_map(array('CJSON', 'nameValue'), + array_keys($vars), + array_values($vars))) + . '}'; + + default: + return ''; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + protected static function nameValue($name, $value) + { + return self::encode(strval($name)) . ':' . self::encode($value); + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + protected static function reduceString($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * @param boolean whether to use associative array to represent object data + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + public static function decode($str, $useArray=true) + { + $str = self::reduceString($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').+(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c+2), 2))) + . chr(hexdec(substr($chrs, ($c+4), 2))); + $utf8 .= self::utf16beToUTF8($utf16); + $c+=5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(self::JSON_IN_ARR); + $arr = array(); + } else { + if ($useArray) { + $stk = array(self::JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(self::JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => self::JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = self::reduceString($chrs); + + if ($chrs == '') { + if (reset($stk) == self::JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == self::JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => self::JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == self::JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, self::decode($slice)); + + } elseif (reset($stk) == self::JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = self::decode($parts[1]); + $val = self::decode($parts[2]); + + if ($useArray) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = self::decode($parts[2]); + + if ($useArray) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != self::JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => self::JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == self::JSON_IN_STR) && + (($chrs{$c - 1} != "\\") || + ($chrs{$c - 1} == "\\" && $chrs{$c - 2} == "\\"))) { + // found a quote, we're in a string, and it's not escaped + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => self::JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == self::JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => self::JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == self::JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => self::JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == self::JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == self::JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * This function returns any UTF-8 encoded text as a list of + * Unicode values: + * + * @author Scott Michael Reynen + * @link http://www.randomchaos.com/document.php?source=php_and_unicode + * @see unicodeToUTF8() + */ + protected static function utf8ToUnicode( &$str ) + { + $unicode = array(); + $values = array(); + $lookingFor = 1; + + for ($i = 0; $i < strlen( $str ); $i++ ) + { + $thisValue = ord( $str[ $i ] ); + if ( $thisValue < 128 ) + $unicode[] = $thisValue; + else + { + if ( count( $values ) == 0 ) + $lookingFor = ( $thisValue < 224 ) ? 2 : 3; + $values[] = $thisValue; + if ( count( $values ) == $lookingFor ) + { + $number = ( $lookingFor == 3 ) ? + ( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ): + ( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 ); + $unicode[] = $number; + $values = array(); + $lookingFor = 1; + } + } + } + return $unicode; + } + + /** + * This function converts a Unicode array back to its UTF-8 representation + * + * @author Scott Michael Reynen + * @link http://www.randomchaos.com/document.php?source=php_and_unicode + * @see utf8ToUnicode() + */ + protected static function unicodeToUTF8( &$str ) + { + $utf8 = ''; + foreach( $str as $unicode ) + { + if ( $unicode < 128 ) + { + $utf8.= chr( $unicode ); + } + elseif ( $unicode < 2048 ) + { + $utf8.= chr( 192 + ( ( $unicode - ( $unicode % 64 ) ) / 64 ) ); + $utf8.= chr( 128 + ( $unicode % 64 ) ); + } + else + { + $utf8.= chr( 224 + ( ( $unicode - ( $unicode % 4096 ) ) / 4096 ) ); + $utf8.= chr( 128 + ( ( ( $unicode % 4096 ) - ( $unicode % 64 ) ) / 64 ) ); + $utf8.= chr( 128 + ( $unicode % 64 ) ); + } + } + return $utf8; + } + + /** + * UTF-8 to UTF-16BE conversion. + * + * Maybe really UCS-2 without mb_string due to utf8ToUnicode limits + */ + protected static function utf8ToUTF16BE(&$str, $bom = false) + { + $out = $bom ? "\xFE\xFF" : ''; + if(function_exists('mb_convert_encoding')) + return $out.mb_convert_encoding($str,'UTF-16BE','UTF-8'); + + $uni = self::utf8ToUnicode($str); + foreach($uni as $cp) + $out .= pack('n',$cp); + return $out; + } + + /** + * UTF-8 to UTF-16BE conversion. + * + * Maybe really UCS-2 without mb_string due to utf8ToUnicode limits + */ + protected static function utf16beToUTF8(&$str) + { + $uni = unpack('n*',$str); + return self::unicodeToUTF8($uni); + } +} diff --git a/web/framework-1.0.8/web/helpers/CJavaScript.php b/web/framework-1.0.8/web/helpers/CJavaScript.php new file mode 100644 index 0000000..118ce03 --- /dev/null +++ b/web/framework-1.0.8/web/helpers/CJavaScript.php @@ -0,0 +1,128 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CJavaScript is a helper class containing JavaScript-related handling functions. + * + * @author Qiang Xue + * @version $Id: CJavaScript.php 811 2009-03-10 12:11:38Z qiang.xue $ + * @package system.web.helpers + * @since 1.0 + */ +class CJavaScript +{ + /** + * Quotes a javascript string. + * After processing, the string can be safely enclosed within a pair of + * quotation marks and serve as a javascript string. + * @param string string to be quoted + * @param boolean whether this string is used as a URL + * @return string the quoted string + */ + public static function quote($js,$forUrl=false) + { + if($forUrl) + return strtr($js,array('%'=>'%25',"\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\')); + else + return strtr($js,array("\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\')); + } + + /** + * Encodes a PHP variable into javascript representation. + * + * Example: + *
    +	 * $options=array('key1'=>true,'key2'=>123,'key3'=>'value');
    +	 * echo CJavaScript::encode($options);
    +	 * // The following javascript code would be generated:
    +	 * // {'key1':true,'key2':123,'key3':'value'}
    +	 * 
    + * + * For highly complex data structures use {@link jsonEncode} and {@link jsonDecode} + * to serialize and unserialize. + * + * @param mixed PHP variable to be encoded + * @return string the encoded string + */ + public static function encode($value) + { + if(is_string($value)) + { + if(strpos($value,'js:')===0) + return substr($value,3); + else + return "'".self::quote($value)."'"; + } + else if($value===null) + return 'null'; + else if(is_bool($value)) + return $value?'true':'false'; + else if(is_integer($value)) + return "$value"; + else if(is_float($value)) + { + if($value===-INF) + return 'Number.NEGATIVE_INFINITY'; + else if($value===INF) + return 'Number.POSITIVE_INFINITY'; + else + return "$value"; + } + else if(is_object($value)) + return self::encode(get_object_vars($value)); + else if(is_array($value)) + { + $es=array(); + if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) + { + foreach($value as $k=>$v) + $es[]="'".self::quote($k)."':".self::encode($v); + return '{'.implode(',',$es).'}'; + } + else + { + foreach($value as $v) + $es[]=self::encode($v); + return '['.implode(',',$es).']'; + } + } + else + return ''; + } + + /** + * Returns the JSON representation of the PHP data. + * @param mixed the data to be encoded + * @return string the JSON representation of the PHP data. + */ + public static function jsonEncode($data) + { + if(function_exists('json_encode')) + return json_encode($data); + else + { + return CJSON::encode($data); + } + } + + /** + * Decodes a JSON string. + * @param string the data to be decoded + * @param boolean whether to use associative array to represent object data + * @return mixed the decoded PHP data + */ + public static function jsonDecode($data,$useArray=true) + { + if(function_exists('json_decode')) + return json_decode($data,$useArray); + else + return CJSON::decode($data,$useArray); + } +} diff --git a/web/framework-1.0.8/web/renderers/CPradoViewRenderer.php b/web/framework-1.0.8/web/renderers/CPradoViewRenderer.php new file mode 100644 index 0000000..8d920fd --- /dev/null +++ b/web/framework-1.0.8/web/renderers/CPradoViewRenderer.php @@ -0,0 +1,245 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CPradoViewRenderer implements a view renderer that allows users to use a template syntax similar to PRADO templates. + * + * To use CPradoViewRenderer, configure it as an application component named "viewRenderer" in the application configuration: + *
    + * array(
    + *     'components'=>array(
    + *         ......
    + *         'viewRenderer'=>array(
    + *             'class'=>'CPradoViewRenderer',
    + *         ),
    + *     ),
    + * )
    + * 
    + * + * CPradoViewRenderer allows you to write view files with the following syntax: + *
    + * // PHP tags:
    + * <%= expression %>
    + * // <?php echo expression ?>
    + * <% statement %>
    + * // <?php statement ?>
    + *
    + * // component tags:
    + * <com:WigetClass name1="value1" name2='value2' name3={value3} >
    + * // <?php $this->beginWidget('WigetClass',
    + * // array('name1'=>"value1", 'name2'=>'value2', 'name3'=>value3)); ?>
    + * </com:WigetClass >
    + * // <?php $this->endWidget('WigetClass'); ?>
    + * <com:WigetClass name1="value1" name2='value2' name3={value3} />
    + * // <?php $this->widget('WigetClass',
    + * // array('name1'=>"value1", 'name2'=>'value2', 'name3'=>value3)); ?>
    + *
    + * // cache tags:
    + * <cache:fragmentID name1="value1" name2='value2' name3={value3} >
    + * // <?php if($this->beginCache('fragmentID',
    + * // array('name1'=>"value1", 'name2'=>'value2', 'name3'=>value3))): ?>
    + * </cache:fragmentID >
    + * // <?php $this->endCache('fragmentID'); endif; ?>
    + *
    + * // clip tags:
    + * <clip:clipID >
    + * // <?php $this->beginClip('clipID'); ?>
    + * </clip:clipID >
    + * // <?php $this->endClip('clipID'); ?>
    + *
    + * // comment tags:
    + * <!--- comments --->
    + * // the whole tag will be stripped off
    + * 
    + * + * @author Steve Heyns http://customgothic.com/ + * @author Qiang Xue + * @version $Id: CPradoViewRenderer.php 795 2009-03-08 13:38:03Z qiang.xue $ + * @package system.web.renderers + * @since 1.0 + */ +class CPradoViewRenderer extends CViewRenderer +{ + private $_input; + private $_output; + private $_sourceFile; + + /** + * Parses the source view file and saves the results as another file. + * This method is required by the parent class. + * @param string the source view file path + * @param string the resulting view file path + */ + protected function generateViewFile($sourceFile,$viewFile) + { + static $regexRules=array( + '<%=?\s*(.*?)\s*%>', // PHP statements or expressions + '<\/?(com|cache|clip):([\w\.]+)\s*((?:\s*\w+\s*=\s*\'.*?(?', // component tags + '', // template comments + ); + $this->_sourceFile=$sourceFile; + $this->_input=file_get_contents($sourceFile); + $n=preg_match_all('/'.implode('|',$regexRules).'/msS',$this->_input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + $textStart=0; + $this->_output="\n"; + for($i=0;$i<$n;++$i) + { + $match=&$matches[$i]; + $str=$match[0][0]; + $matchStart=$match[0][1]; + $matchEnd=$matchStart+strlen($str)-1; + + if($matchStart>$textStart) + $this->_output.=substr($this->_input,$textStart,$matchStart-$textStart); + $textStart=$matchEnd+1; + + if(strpos($str,'_output.=$this->processBeginWidget($type,$match[4][0],$match[2][1]); + else + $this->_output.=$this->processWidget($type,$match[4][0],$match[2][1]); + } + else if(strpos($str,'_output.=$this->processEndWidget($match[3][0],$match[2][1]); + else if(strpos($str,'_output.=$this->processBeginCache($id,$match[4][0],$match[2][1]); + else + $this->_output.=$this->processCache($id,$match[4][0],$match[2][1]); + } + else if(strpos($str,'_output.=$this->processEndCache($match[3][0],$match[2][1]); + else if(strpos($str,'_output.=$this->processBeginClip($id,$match[4][0],$match[2][1]); + else + $this->_output.=$this->processClip($id,$match[4][0],$match[2][1]); + } + else if(strpos($str,'_output.=$this->processEndClip($match[3][0],$match[2][1]); + else if(strpos($str,'<%=')===0) // expression + $this->_output.=$this->processExpression($match[1][0],$match[1][1]); + else if(strpos($str,'<%')===0) // statement + $this->_output.=$this->processStatement($match[1][0],$match[1][1]); + } + if($textStart_input)) + $this->_output.=substr($this->_input,$textStart); + + file_put_contents($viewFile,$this->_output); + } + + private function processWidget($type,$attributes,$offset) + { + $attrs=$this->processAttributes($attributes); + if(empty($attrs)) + return $this->generatePhpCode("\$this->widget('$type');",$offset); + else + return $this->generatePhpCode("\$this->widget('$type', array($attrs));",$offset); + } + + private function processBeginWidget($type,$attributes,$offset) + { + $attrs=$this->processAttributes($attributes); + if(empty($attrs)) + return $this->generatePhpCode("\$this->beginWidget('$type');",$offset); + else + return $this->generatePhpCode("\$this->beginWidget('$type', array($attrs));",$offset); + } + + private function processEndWidget($type,$offset) + { + return $this->generatePhpCode("\$this->endWidget('$type');",$offset); + } + + private function processCache($id,$attributes,$offset) + { + return $this->processBeginCache($id,$attributes,$offset) . $this->processEndCache($id,$offset); + } + + private function processBeginCache($id,$attributes,$offset) + { + $attrs=$this->processAttributes($attributes); + if(empty($attrs)) + return $this->generatePhpCode("if(\$this->beginCache('$id')):",$offset); + else + return $this->generatePhpCode("if(\$this->beginCache('$id', array($attrs))):",$offset); + } + + private function processEndCache($id,$offset) + { + return $this->generatePhpCode("\$this->endCache('$id'); endif;",$offset); + } + + private function processClip($id,$attributes,$offset) + { + return $this->processBeginClip($id,$attributes,$offset) . $this->processEndClip($id,$offset); + } + + private function processBeginClip($id,$attributes,$offset) + { + $attrs=$this->processAttributes($attributes); + if(empty($attrs)) + return $this->generatePhpCode("\$this->beginClip('$id');",$offset); + else + return $this->generatePhpCode("\$this->beginClip('$id', array($attrs));",$offset); + } + + private function processEndClip($id,$offset) + { + return $this->generatePhpCode("\$this->endClip('$id');",$offset); + } + + private function processExpression($expression,$offset) + { + return $this->generatePhpCode('echo '.$expression,$offset); + } + + private function processStatement($statement,$offset) + { + return $this->generatePhpCode($statement,$offset); + } + + private function generatePhpCode($code,$offset) + { + $line=$this->getLineNumber($offset); + $code=str_replace('__FILE__',var_export($this->_sourceFile,true),$code); + return ""; + } + + private function processAttributes($str) + { + static $pattern='/(\w+)\s*=\s*(\'.*?(?".str_replace('__FILE__',$this->_sourceFile,substr($value,1,-1)); + else + $attributes[]="'$name'=>$value"; + } + return implode(', ',$attributes); + } + + private function getLineNumber($offset) + { + return count(explode("\n",substr($this->_input,0,$offset))); + } +} diff --git a/web/framework-1.0.8/web/renderers/CViewRenderer.php b/web/framework-1.0.8/web/renderers/CViewRenderer.php new file mode 100644 index 0000000..0133102 --- /dev/null +++ b/web/framework-1.0.8/web/renderers/CViewRenderer.php @@ -0,0 +1,93 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CViewRenderer is the base class for view renderer classes. + * + * A view renderer is an application component that renders views written + * in a customized syntax. + * + * Once installing a view renderer as a 'viewRenderer' application component, + * the normal view rendering process will be intercepted by the renderer. + * The renderer will first parse the source view file and then render the + * the resulting view file. + * + * Parsing results are saved as temporary files that may be stored + * under the application runtime directory or together with the source view file. + * + * @author Steve Heyns http://customgothic.com/ + * @author Qiang Xue + * @version $Id: CViewRenderer.php 929 2009-04-11 02:00:47Z qiang.xue@gmail.com $ + * @package system.web.renderers + * @since 1.0 + */ +abstract class CViewRenderer extends CApplicationComponent implements IViewRenderer +{ + /** + * @var boolean whether to store the parsing results in the application's + * runtime directory. Defaults to true. If false, the parsing results will + * be saved as files under the same directory as the source view files and the + * file names will be the source file names appended with letter 'c'. + */ + public $useRuntimePath=true; + /** + * @var integer the chmod permission for temporary directories and files + * generated during parsing. Defaults to 0755 (owner rwx, group rx and others rx). + */ + public $filePermission=0755; + + /** + * Parses the source view file and saves the results as another file. + * @param string the source view file path + * @param string the resulting view file path + */ + abstract protected function generateViewFile($sourceFile,$viewFile); + + /** + * Renders a view file. + * This method is required by {@link IViewRenderer}. + * @param CBaseController the controller or widget who is rendering the view file. + * @param string the view file path + * @param mixed the data to be passed to the view + * @param boolean whether the rendering result should be returned + * @return mixed the rendering result, or null if the rendering result is not needed. + */ + public function renderFile($context,$sourceFile,$data,$return) + { + if(!is_file($sourceFile) || ($file=realpath($sourceFile))===false) + throw new CException(Yii::t('yii','View file "{file}" does not exist.',array('{file}'=>$sourceFile))); + $viewFile=$this->getViewFile($sourceFile); + if(@filemtime($sourceFile)>@filemtime($viewFile)) + { + $this->generateViewFile($sourceFile,$viewFile); + @chmod($viewFile,$this->filePermission); + } + return $context->renderInternal($viewFile,$data,$return); + } + + /** + * Generates the resulting view file path. + * @param string source view file path + * @return string resulting view file path + */ + protected function getViewFile($file) + { + if($this->useRuntimePath) + { + $crc=sprintf('%x', crc32(get_class($this).Yii::getVersion().dirname($file))); + $viewFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$crc.DIRECTORY_SEPARATOR.basename($file); + if(!is_file($viewFile)) + @mkdir(dirname($viewFile),$this->filePermission,true); + return $viewFile; + } + else + return $file.'c'; + } +} diff --git a/web/framework-1.0.8/web/services/CWebService.php b/web/framework-1.0.8/web/services/CWebService.php new file mode 100644 index 0000000..da950ac --- /dev/null +++ b/web/framework-1.0.8/web/services/CWebService.php @@ -0,0 +1,271 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebService encapsulates SoapServer and provides a WSDL-based web service. + * + * PHP SOAP extension is required. + * + * CWebService makes use of {@link CWsdlGenerator} and can generate the WSDL + * on-the-fly without requiring you to write complex WSDL. + * + * To generate the WSDL based on doc comment blocks in the service provider class, + * call {@link generateWsdl} or {@link renderWsdl}. To process the web service + * requests, call {@link run}. + * + * @author Qiang Xue + * @version $Id: CWebService.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.services + * @since 1.0 + */ +class CWebService extends CComponent +{ + const SOAP_ERROR=1001; + /** + * @var string|object the web service provider class or object. + * If specified as a class name, it can be a path alias. + */ + public $provider; + /** + * @var string the URL for WSDL. This is required by {@link run()}. + */ + public $wsdlUrl; + /** + * @var string the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. + */ + public $serviceUrl; + /** + * @var integer number of seconds that the generated WSDL can remain valid in cache. Defaults to 0, meaning no caching. + */ + public $wsdlCacheDuration=0; + /** + * @var string encoding of the Web service. Defaults to 'UTF-8'. + */ + public $encoding='UTF-8'; + /** + * @var array a list of classes that are declared as complex types in WSDL. + * This should be an array with WSDL types as keys and names of PHP classes as values. + * A PHP class can also be specified as a path alias. + * @see http://www.php.net/manual/en/function.soap-soapserver-construct.php + */ + public $classMap=array(); + /** + * @var string actor of the SOAP service. Defaults to null, meaning not set. + */ + public $actor; + /** + * @var string SOAP version (e.g. '1.1' or '1.2'). Defaults to null, meaning not set. + */ + public $soapVersion; + /** + * @var integer the persistence mode of the SOAP server. + * @see http://www.php.net/manual/en/function.soap-soapserver-setpersistence.php + */ + public $persistence; + + private $_method; + + + /** + * Constructor. + * @param mixed the web service provider class name or object + * @param string the URL for WSDL. This is required by {@link run()}. + * @param string the URL for the Web service. This is required by {@link generateWsdl()} and {@link renderWsdl()}. + */ + public function __construct($provider,$wsdlUrl,$serviceUrl) + { + $this->provider=$provider; + $this->wsdlUrl=$wsdlUrl; + $this->serviceUrl=$serviceUrl; + } + + /** + * The PHP error handler. + * @param CErrorEvent the PHP error event + */ + public function handleError($event) + { + $event->handled=true; + $message=$event->message; + if(YII_DEBUG) + { + $trace=debug_backtrace(); + if(isset($trace[2]) && isset($trace[2]['file']) && isset($trace[2]['line'])) + $message.=' ('.$trace[2]['file'].':'.$trace[2]['line'].')'; + } + throw new CException($message,self::SOAP_ERROR); + } + + /** + * Generates and displays the WSDL as defined by the provider. + * @see generateWsdl + */ + public function renderWsdl() + { + $wsdl=$this->generateWsdl(); + header('Content-Type: text/xml;charset='.$this->encoding); + echo $wsdl; + } + + /** + * Generates the WSDL as defined by the provider. + * The cached version may be used if the WSDL is found valid in cache. + * @return string the generated WSDL + * @see wsdlCacheDuration + */ + public function generateWsdl() + { + $providerClass=is_object($this->provider) ? get_class($this->provider) : Yii::import($this->provider,true); + if($this->wsdlCacheDuration>0 && ($cache=Yii::app()->getCache())!==null) + { + $key='Yii.CWebService.'.$providerClass.$this->serviceUrl.$this->encoding; + if(($wsdl=$cache->get($key))!==false) + return $wsdl; + } + $generator=new CWsdlGenerator; + $wsdl=$generator->generateWsdl($providerClass,$this->serviceUrl,$this->encoding); + if(isset($key)) + $cache->set($key,$wsdl,$this->wsdlCacheDuration); + return $wsdl; + } + + /** + * Handles the web service request. + */ + public function run() + { + header('Content-Type: text/xml;charset='.$this->encoding); + if(YII_DEBUG) + ini_set("soap.wsdl_cache_enabled",0); + $server=new SoapServer($this->wsdlUrl,$this->getOptions()); + Yii::app()->attachEventHandler('onError',array($this,'handleError')); + try + { + if($this->persistence!==null) + $server->setPersistence($this->persistence); + if(is_string($this->provider)) + $provider=Yii::createComponent($this->provider); + else + $provider=$this->provider; + + if(method_exists($server,'setObject')) + $server->setObject($provider); + else + $server->setClass('CSoapObjectWrapper',$provider); + + if($provider instanceof IWebServiceProvider) + { + if($provider->beforeWebMethod($this)) + { + $server->handle(); + $provider->afterWebMethod($this); + } + } + else + $server->handle(); + } + catch(Exception $e) + { + if($e->getCode()===self::SOAP_ERROR) // a PHP error + $message=$e->getMessage(); + else + { + $message=$e->getMessage().' ('.$e->getFile().':'.$e->getLine().')'; + // only log for non-PHP-error case because application's error handler already logs it + Yii::log((string)$e,CLogger::LEVEL_ERROR,'application'); + } + if(YII_DEBUG) + $message.="\n".$e->getTraceAsString(); + $server->fault(get_class($e),$message); + } + } + + /** + * @return string the currently requested method name. Empty if no method is being requested. + */ + public function getMethodName() + { + if($this->_method===null) + { + if(isset($HTTP_RAW_POST_DATA)) + $request=$HTTP_RAW_POST_DATA; + else + $request=file_get_contents('php://input'); + if(preg_match('/<.*?:Body[^>]*>\s*<.*?:(\w+)/mi',$request,$matches)) + $this->_method=$matches[1]; + else + $this->_method=''; + } + return $this->_method; + } + + /** + * @return array options for creating SoapServer instance + * @see http://www.php.net/manual/en/function.soap-soapserver-construct.php + */ + protected function getOptions() + { + $options=array(); + if($this->soapVersion==='1.1') + $options['soap_version']=SOAP_1_1; + else if($this->soapVersion==='1.2') + $options['soap_version']=SOAP_1_2; + if($this->actor!==null) + $options['actor']=$this->actor; + $options['encoding']=$this->encoding; + foreach($this->classMap as $type=>$className) + { + $className=Yii::import($className,true); + if(is_int($type)) + $type=$className; + $options['classmap'][$type]=$className; + } + return $options; + } +} + + +/** + * CSoapObjectWrapper is a wrapper class internally used when SoapServer::setObject() is not defined. + * + * @author Qiang Xue + * @version $Id: CWebService.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.services + * @since 1.0.5 + */ +class CSoapObjectWrapper +{ + /** + * @var object the service provider + */ + public $object=null; + + /** + * Constructor. + * @param object the service provider + */ + public function __construct($object) + { + $this->object=$object; + } + + /** + * PHP __call magic method. + * This method calls the service provider to execute the actual logic. + * @param string method name + * @param array method arguments + * @return mixed method return value + */ + public function __call($name,$arguments) + { + return call_user_func_array(array($this->object,$name),$arguments); + } +} + diff --git a/web/framework-1.0.8/web/services/CWebServiceAction.php b/web/framework-1.0.8/web/services/CWebServiceAction.php new file mode 100644 index 0000000..9015c6d --- /dev/null +++ b/web/framework-1.0.8/web/services/CWebServiceAction.php @@ -0,0 +1,130 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWebServiceAction implements an action that provides Web services. + * + * CWebServiceAction serves for two purposes. On the one hand, it displays + * the WSDL content specifying the Web service APIs. On the other hand, it + * invokes the requested Web service API. A GET parameter named ws + * is used to differentiate these two aspects: the existence of the GET parameter + * indicates performing the latter action. + * + * By default, CWebServiceAction will use the current controller as + * the Web service provider. See {@link CWsdlGenerator} on how to declare + * methods that can be remotely invoked. + * + * Note, PHP SOAP extension is required for this action. + * + * @author Qiang Xue + * @version $Id: CWebServiceAction.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.services + * @since 1.0 + */ +class CWebServiceAction extends CAction +{ + /** + * @var mixed the Web service provider object or class name. + * If specified as a class name, it can be a path alias. + * Defaults to null, meaning the current controller is used as the service provider. + * If the provider implements the interface {@link IWebServiceProvider}, + * it will be able to intercept the remote method invocation and perform + * additional tasks (e.g. authentication, logging). + */ + public $provider; + /** + * @var string the URL for the Web service. Defaults to null, meaning + * the URL for this action is used to provide Web services. + * In this case, a GET parameter named {@link serviceVar} will be used to + * deteremine whether the current request is for WSDL or Web service. + */ + public $serviceUrl; + /** + * @var string the URL for WSDL. Defaults to null, meaning + * the URL for this action is used to serve WSDL document. + */ + public $wsdlUrl; + /** + * @var string the name of the GET parameter that differentiates a WSDL request + * from a Web service request. If this GET parameter exists, the request is considered + * as a Web service request; otherwise, it is a WSDL request. Defaults to 'ws'. + */ + public $serviceVar='ws'; + /** + * @var array a list of PHP classes that are declared as complex types in WSDL. + * This should be an array with WSDL types as keys and names of PHP classes as values. + * A PHP class can also be specified as a path alias. + * @see http://www.php.net/manual/en/soapclient.soapclient.php + */ + public $classMap; + /** + * @var array the initial property values for the {@link CWebService} object. + * The array keys are property names of {@link CWebService} and the array values + * are the corresponding property initial values. + */ + public $serviceOptions=array(); + + private $_service; + + + /** + * Runs the action. + * If the GET parameter {@link serviceVar} exists, the action handle the remote method invocation. + * If not, the action will serve WSDL content; + */ + public function run() + { + $hostInfo=Yii::app()->getRequest()->getHostInfo(); + $controller=$this->getController(); + if(($serviceUrl=$this->serviceUrl)===null) + $serviceUrl=$hostInfo.$controller->createUrl($this->getId(),array($this->serviceVar=>1)); + if(($wsdlUrl=$this->wsdlUrl)===null) + $wsdlUrl=$hostInfo.$controller->createUrl($this->getId()); + if(($provider=$this->provider)===null) + $provider=$controller; + + $this->_service=$this->createWebService($provider,$wsdlUrl,$serviceUrl); + + if(is_array($this->classMap)) + $this->_service->classMap=$this->classMap; + + foreach($this->serviceOptions as $name=>$value) + $this->_service->$name=$value; + + if(isset($_GET[$this->serviceVar])) + $this->_service->run(); + else + $this->_service->renderWsdl(); + + Yii::app()->end(); + } + + /** + * Returns the Web service instance currently being used. + * @return CWebService the Web service instance + */ + public function getService() + { + return $this->_service; + } + + /** + * Creates a {@link CWebService} instance. + * You may override this method to customize the created instance. + * @param mixed the web service provider class name or object + * @param string the URL for WSDL. + * @param string the URL for the Web service. + * @return CWebService the Web service instance + */ + protected function createWebService($provider,$wsdlUrl,$serviceUrl) + { + return new CWebService($provider,$wsdlUrl,$serviceUrl); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/services/CWsdlGenerator.php b/web/framework-1.0.8/web/services/CWsdlGenerator.php new file mode 100644 index 0000000..ab93c3c --- /dev/null +++ b/web/framework-1.0.8/web/services/CWsdlGenerator.php @@ -0,0 +1,373 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWsdlGenerator generates the WSDL for a given service class. + * + * The WSDL generation is based on the doc comments found in the service class file. + * In particular, it recognizes the '@soap' tag in the comment and extracts + * API method and type definitions. + * + * In a service class, a remote invokable method must be a public method with a doc + * comment block containing the '@soap' tag. In the doc comment, the type and name + * of every input parameter and the type of the return value should be declared using + * the standard phpdoc format. + * + * CWsdlGenerator recognizes the following primitive types (case-sensitive) in + * the parameter and return type declarations: + *
      + *
    • str/string: maps to xsd:string;
    • + *
    • int/integer: maps to xsd:int;
    • + *
    • float/double: maps to xsd:float;
    • + *
    • bool/boolean: maps to xsd:boolean;
    • + *
    • date: maps to xsd:date;
    • + *
    • time: maps to xsd:time;
    • + *
    • datetime: maps to xsd:dateTime;
    • + *
    • array: maps to xsd:string;
    • + *
    • object: maps to xsd:struct;
    • + *
    • mixed: maps to xsd:anyType.
    • + *
    + * + * If a type is not a primitive type, it is considered as a class type, and + * CWsdlGenerator will look for its property declarations. Only public properties + * are considered, and they each must be associated with a doc comment block containg + * the '@soap' tag. The doc comment block should declare the type of the property. + * + * CWsdlGenerator recognizes the array type with the following format: + *
    + * typeName[]: maps to tns:typeNameArray
    + * 
    + * + * The following is an example declaring a remote invokable method: + *
    + * / **
    + *   * A foo method.
    + *   * @param string name of something
    + *   * @param string value of something
    + *   * @return string[] some array
    + *   * @soap
    + *   * /
    + * public function foo($name,$value) {...}
    + * 
    + * + * And the following is an example declaring a class with remote accessible properties: + *
    + * class Foo {
    + *     / **
    + *       * @var string name of foo
    + *       * @soap
    + *       * /
    + *     public $name;
    + *     / **
    + *       * @var Member[] members of foo
    + *       * @soap
    + *       * /
    + *     public $members;
    + * }
    + * 
    + * In the above, the 'members' property is an array of 'Member' objects. Since 'Member' is not + * a primitive type, CWsdlGenerator will look further to find the definition of 'Member'. + * + * @author Qiang Xue + * @version $Id: CWsdlGenerator.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.services + * @since 1.0 + */ +class CWsdlGenerator extends CComponent +{ + private $_operations; + private $_types; + private $_messages; + private $_namespace; + private $_serviceName; + + /** + * Generates the WSDL for the given class. + * @param string class name + * @param string Web service URL + * @param string encoding of the WSDL. Defaults to 'UTF-8'. + * @return string the generated WSDL + */ + public function generateWsdl($className, $serviceUrl, $encoding='UTF-8') + { + $this->_operations=array(); + $this->_types=array(); + $this->_messages=array(); + $this->_serviceName=$className; + $this->_namespace="urn:{$className}wsdl"; + + $reflection=new ReflectionClass($className); + foreach($reflection->getMethods() as $method) + { + if($method->isPublic()) + $this->processMethod($method); + } + + return $this->buildDOM($serviceUrl,$encoding)->saveXML(); + } + + private function processMethod($method) + { + $comment=$method->getDocComment(); + if(strpos($comment,'@soap')===false) + return; + + $methodName=$method->getName(); + $comment=preg_replace('/^\s*\**(\s*?$|\s*)/m','',$comment); + $params=$method->getParameters(); + $message=array(); + $n=preg_match_all('/^@param\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches); + if($n>count($params)) + $n=count($params); + for($i=0;$i<$n;++$i) + $message[$params[$i]->getName()]=array($this->processType($matches[1][$i]), trim($matches[3][$i])); // name => type, doc + + $this->_messages[$methodName.'Request']=$message; + + if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches)) + $return=array($this->processType($matches[1]),trim($matches[2])); // type, doc + else + $return=null; + $this->_messages[$methodName.'Response']=array('return'=>$return); + + if(preg_match('/^\/\*+\s*([^@]*?)\n@/s',$comment,$matches)) + $doc=trim($matches[1]); + else + $doc=''; + $this->_operations[$methodName]=$doc; + } + + private function processType($type) + { + static $typeMap=array( + 'string'=>'xsd:string', + 'str'=>'xsd:string', + 'int'=>'xsd:int', + 'integer'=>'xsd:integer', + 'float'=>'xsd:float', + 'double'=>'xsd:float', + 'bool'=>'xsd:boolean', + 'boolean'=>'xsd:boolean', + 'date'=>'xsd:date', + 'time'=>'xsd:time', + 'datetime'=>'xsd:dateTime', + 'array'=>'soap-enc:Array', + 'object'=>'xsd:struct', + 'mixed'=>'xsd:anyType', + ); + if(isset($typeMap[$type])) + return $typeMap[$type]; + else if(isset($this->_types[$type])) + return is_array($this->_types[$type]) ? 'tns:'.$type : $this->_types[$type]; + else if(($pos=strpos($type,'[]'))!==false) // if it is an array + { + $type=substr($type,0,$pos); + if(isset($typeMap[$type])) + $this->_types[$type.'[]']='xsd:'.$type.'Array'; + else + { + $this->_types[$type.'[]']='tns:'.$type.'Array'; + $this->processType($type); + } + return $this->_types[$type.'[]']; + } + else // class type + { + $type=Yii::import($type,true); + $this->_types[$type]=array(); + $class=new ReflectionClass($type); + foreach($class->getProperties() as $property) + { + $comment=$property->getDocComment(); + if($property->isPublic() && strpos($comment,'@soap')!==false) + { + if(preg_match('/@var\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/mi',$comment,$matches)) + $this->_types[$type][$property->getName()]=array($this->processType($matches[1]),trim($matches[3])); // name => type, doc + } + } + return 'tns:'.$type; + } + } + + private function buildDOM($serviceUrl,$encoding) + { + $xml=" +_serviceName}\" targetNamespace=\"{$this->_namespace}\" + xmlns=\"http://schemas.xmlsoap.org/wsdl/\" + xmlns:tns=\"{$this->_namespace}\" + xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" + xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" + xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" + xmlns:soap-enc=\"http://schemas.xmlsoap.org/soap/encoding/\">"; + + $dom=new DOMDocument(); + $dom->loadXml($xml); + $this->addTypes($dom); + + $this->addMessages($dom); + $this->addPortTypes($dom); + $this->addBindings($dom); + $this->addService($dom,$serviceUrl); + + return $dom; + } + + private function addTypes($dom) + { + if($this->_types===array()) + return; + $types=$dom->createElement('wsdl:types'); + $schema=$dom->createElement('xsd:schema'); + $schema->setAttribute('targetNamespace',$this->_namespace); + foreach($this->_types as $phpType=>$xmlType) + { + if(is_string($xmlType) && strrpos($xmlType,'Array')!==strlen($xmlType)-5) + continue; // simple type + $complexType=$dom->createElement('xsd:complexType'); + if(is_string($xmlType)) + { + if(($pos=strpos($xmlType,'tns:'))!==false) + $complexType->setAttribute('name',substr($xmlType,4)); + else + $complexType->setAttribute('name',$xmlType); + $complexContent=$dom->createElement('xsd:complexContent'); + $restriction=$dom->createElement('xsd:restriction'); + $restriction->setAttribute('base','soap-enc:Array'); + $attribute=$dom->createElement('xsd:attribute'); + $attribute->setAttribute('ref','soap-enc:arrayType'); + $attribute->setAttribute('wsdl:arrayType',substr($xmlType,0,strlen($xmlType)-5).'[]'); + $restriction->appendChild($attribute); + $complexContent->appendChild($restriction); + $complexType->appendChild($complexContent); + } + else if(is_array($xmlType)) + { + $complexType->setAttribute('name',$phpType); + $all=$dom->createElement('xsd:all'); + foreach($xmlType as $name=>$type) + { + $element=$dom->createElement('xsd:element'); + $element->setAttribute('name',$name); + $element->setAttribute('type',$type[0]); + $all->appendChild($element); + } + $complexType->appendChild($all); + } + $schema->appendChild($complexType); + $types->appendChild($schema); + } + + $dom->documentElement->appendChild($types); + } + + private function addMessages($dom) + { + foreach($this->_messages as $name=>$message) + { + $element=$dom->createElement('wsdl:message'); + $element->setAttribute('name',$name); + foreach($this->_messages[$name] as $partName=>$part) + { + if(is_array($part)) + { + $partElement=$dom->createElement('wsdl:part'); + $partElement->setAttribute('name',$partName); + $partElement->setAttribute('type',$part[0]); + $element->appendChild($partElement); + } + } + $dom->documentElement->appendChild($element); + } + } + + private function addPortTypes($dom) + { + $portType=$dom->createElement('wsdl:portType'); + $portType->setAttribute('name',$this->_serviceName.'PortType'); + $dom->documentElement->appendChild($portType); + foreach($this->_operations as $name=>$doc) + $portType->appendChild($this->createPortElement($dom,$name,$doc)); + } + + private function createPortElement($dom,$name,$doc) + { + $operation=$dom->createElement('wsdl:operation'); + $operation->setAttribute('name',$name); + + $input = $dom->createElement('wsdl:input'); + $input->setAttribute('message', 'tns:'.$name.'Request'); + $output = $dom->createElement('wsdl:output'); + $output->setAttribute('message', 'tns:'.$name.'Response'); + + $operation->appendChild($dom->createElement('wsdl:documentation',$doc)); + $operation->appendChild($input); + $operation->appendChild($output); + + return $operation; + } + + private function addBindings($dom) + { + $binding=$dom->createElement('wsdl:binding'); + $binding->setAttribute('name',$this->_serviceName.'Binding'); + $binding->setAttribute('type','tns:'.$this->_serviceName.'PortType'); + + $soapBinding=$dom->createElement('soap:binding'); + $soapBinding->setAttribute('style','rpc'); + $soapBinding->setAttribute('transport','http://schemas.xmlsoap.org/soap/http'); + $binding->appendChild($soapBinding); + + $dom->documentElement->appendChild($binding); + + foreach($this->_operations as $name=>$doc) + $binding->appendChild($this->createOperationElement($dom,$name)); + } + + private function createOperationElement($dom,$name) + { + $operation=$dom->createElement('wsdl:operation'); + $operation->setAttribute('name', $name); + $soapOperation = $dom->createElement('soap:operation'); + $soapOperation->setAttribute('soapAction', $this->_namespace.'#'.$name); + $soapOperation->setAttribute('style','rpc'); + + $input = $dom->createElement('wsdl:input'); + $output = $dom->createElement('wsdl:output'); + + $soapBody = $dom->createElement('soap:body'); + $soapBody->setAttribute('use', 'encoded'); + $soapBody->setAttribute('namespace', $this->_namespace); + $soapBody->setAttribute('encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/'); + $input->appendChild($soapBody); + $output->appendChild(clone $soapBody); + + $operation->appendChild($soapOperation); + $operation->appendChild($input); + $operation->appendChild($output); + + return $operation; + } + + private function addService($dom,$serviceUrl) + { + $service=$dom->createElement('wsdl:service'); + $service->setAttribute('name', $this->_serviceName.'Service'); + + $port=$dom->createElement('wsdl:port'); + $port->setAttribute('name', $this->_serviceName.'Port'); + $port->setAttribute('binding', 'tns:'.$this->_serviceName.'Binding'); + + $soapAddress=$dom->createElement('soap:address'); + $soapAddress->setAttribute('location',$serviceUrl); + $port->appendChild($soapAddress); + $service->appendChild($port); + $dom->documentElement->appendChild($service); + } +} diff --git a/web/framework-1.0.8/web/widgets/CAutoComplete.php b/web/framework-1.0.8/web/widgets/CAutoComplete.php new file mode 100644 index 0000000..3614dc4 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CAutoComplete.php @@ -0,0 +1,285 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAutoComplete generates an auto-complete text field. + * + * CAutoComplete is based on the {@link http://plugins.jquery.com/project/autocompletex jQuery Autocomplete}. + * + * @author Qiang Xue + * @version $Id: CAutoComplete.php 913 2009-04-04 02:01:48Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CAutoComplete extends CInputWidget +{ + /** + * @var boolean whether to show the autocomplete using a text area. Defaults to false, + * meaning a text field is used. + * @since 1.0.4 + */ + public $textArea=false; + /** + * @var array data that would be saved as client-side data to provide candidate selections. + * Each array element can be string or an associative array. + * The {@link url} property will be ignored if this property is set. + * @see url + */ + public $data; + /** + * @var string|array the URL that can return the candidate selections. + * A 'q' GET parameter will be sent with the URL which contains what the user has entered so far. + * If the URL is given as an array, it is considered as a route to a controller action and will + * be used to generate a URL using {@link CController::createUrl}; + * If the URL is an empty string, the currently requested URL is used. + * This property will be ignored if {@link data} is set. + * @see data + */ + public $url=''; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var integer the minimum number of characters a user has to type before + * the autocompleter activates. Defaults to 1. + */ + public $minChars; + /** + * @var integer the delay in milliseconds the autocompleter waits after + * a keystroke to activate itself. Defaults to 400. + */ + public $delay; + /** + * @var integer the number of backend query results to store in cache. + * If set to 1 (the current result), no caching will happen. Must be >= 1. Defaults to 10. + */ + public $cacheLength; + /** + * @var boolean whether or not the autocompleter can use a cache for more + * specific queries. This means that all matches of "foot" are a subset + * of all matches for "foo". Usually this is true, and using this options + * decreases server load and increases performance. Only useful with + * cacheLength settings bigger than one, like 10. Defaults to true. + */ + public $matchSubset; + /** + * @var boolean whether or not the comparison is case sensitive. Important + * only if you use caching. Defaults to false. + */ + public $matchCase; + /** + * @var boolean whether or not the comparison looks inside + * (i.e. does "ba" match "foo bar") the search results. Important only if + * you use caching. Don't mix with autofill. Defaults to false. + */ + public $matchContains; + /** + * @var boolean if set to true, the autocompleter will only allow results that + * are presented by the backend. Note that illegal values result in an empty + * input box. Defaults to false. + */ + public $mustMatch; + /** + * @var boolean if this is set to true, the first autocomplete value will + * be automatically selected on tab/return, even if it has not been handpicked + * by keyboard or mouse action. If there is a handpicked (highlighted) result, + * that result will take precedence. Defaults to true. + */ + public $selectFirst; + /** + * @var array extra parameters for the backend. If you were to specify + * array('bar'=>4), the autocompleter would call the backend with a GET + * parameter 'bar' 4. The param can be a function that is called to calculate + * the param before each request. + */ + public $extraParams; + /** + * @var string a javascript function that provides advanced markup for an item. + * For each row of results, this function will be called. The returned value will + * be displayed inside an LI element in the results list. Autocompleter will + * provide 4 parameters: the results row, the position of the row in the list of + * results (starting at 1), the number of items in the list of results and the search term. + * The default behavior assumes that a single row contains a single value. + */ + public $formatItem; + /** + * @var string a javascript function that can be used to limit the data that autocomplete + * searches for matches. For example, there may be items you want displayed to the user, + * but don't want included in the data that's searched. The function is called with the same arguments + * as {@link formatItem}. Defaults to formatItem. + */ + public $formatMatch; + /** + * @var string a javascript function that provides the formatting for the value to be + * put into the input field. Again three arguments: Data, position (starting with one) and + * total number of data. The default behavior assumes either plain data to use as result + * or uses the same value as provided by formatItem. + */ + public $formatResult; + /** + * @var boolean whether to allow more than one autocompleted-value to enter. Defaults to false. + */ + public $multiple; + /** + * @var string seperator to put between values when using multiple option. Defaults to ", ". + */ + public $multipleSeparator; + /** + * @var integer specify a custom width for the select box. Defaults to the width of the input element. + */ + public $width; + /** + * @var boolean fill the textinput while still selecting a value, replacing the value + * if more is typed or something else is selected. Defaults to false. + */ + public $autoFill; + /** + * @var integer limit the number of items in the select box. Is also sent as + * a "limit" parameter with a remote request. Defaults to 10. + */ + public $max; + /** + * @var boolean|string Whether and how to highlight matches in the select box. + * Set to false to disable. Set to a javascript function to customize. + * The function gets the value as the first argument and the search term as the + * second and must return the formatted value. Defaults to Wraps the search term in a <strong> element. + */ + public $highlight; + /** + * @var boolean whether to scroll when more results than configured via scrollHeight are available. Defaults to true. + */ + public $scroll; + /** + * @var integer height of scrolled autocomplete control in pixels. Defaults to 180. + */ + public $scrollHeight; + /** + * @var string the CSS class for the input element. Defaults to "ac_input". + */ + public $inputClass; + /** + * @var string the CSS class for the dropdown list. Defaults to "ac_results". + */ + public $resultsClass; + /** + * @var string the CSS class used when the data is being loaded from backend. Defaults to "ac_loading". + */ + public $loadingClass; + /** + * @var array additional options that can be passed to the constructor of the autocomplete js object. + * This allows you to override existing functions of the autocomplete js class (e.g. the parse() function) + */ + public $options=array(); + /** + * @var string the chain of method calls that would be appended at the end of the autocomplete constructor. + * For example, ".result(function(...){})" would cause the specified js function to execute + * when the user selects an option. + */ + public $methodChain; + + /** + * Initializes the widget. + * This method registers all needed client scripts and renders + * the autocomplete input. + */ + public function init() + { + list($name,$id)=$this->resolveNameID(); + $this->htmlOptions['id']=$id; + $this->htmlOptions['name']=$name; + + $this->registerClientScript(); + + + if($this->hasModel()) + { + $field=$this->textArea ? 'activeTextArea' : 'activeTextField'; + echo CHtml::$field($this->model,$this->attribute,$this->htmlOptions); + } + else + { + $field=$this->textArea ? 'textArea' : 'textField'; + echo CHtml::$field($name,$this->value,$this->htmlOptions); + } + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + $id=$this->htmlOptions['id']; + + $acOptions=$this->getClientOptions(); + $options=$acOptions===array()?'{}' : CJavaScript::encode($acOptions); + + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('autocomplete'); + if($this->data!==null) + $data=CJavaScript::encode($this->data); + else + { + $url=CHtml::normalizeUrl($this->url); + $data='"'.$url.'"'; + } + $cs->registerScript('Yii.CAutoComplete#'.$id,"jQuery(\"#{$id}\").autocomplete($data,{$options}){$this->methodChain};"); + + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + return; + $cs=Yii::app()->getClientScript(); + if($url===null) + $url=$cs->getCoreScriptUrl().'/autocomplete/jquery.autocomplete.css'; + $cs->registerCssFile($url); + } + + /** + * @return array the javascript options + */ + protected function getClientOptions() + { + static $properties=array( + 'minChars', 'delay', 'cacheLength', 'matchSubset', + 'matchCase', 'matchContains', 'mustMatch', 'selectFirst', + 'extraParams', 'multiple', 'multipleSeparator', 'width', + 'autoFill', 'max', 'scroll', 'scrollHeight', 'inputClass', + 'formatItem', 'formatMatch', 'formatResult', 'highlight', + 'resultsClass', 'loadingClass'); + static $functions=array('formatItem', 'formatMatch', 'formatResult', 'highlight'); + + $options=$this->options; + foreach($properties as $property) + { + if($this->$property!==null) + $options[$property]=$this->$property; + } + foreach($functions as $func) + { + if(is_string($this->$func) && strncmp($this->$func,'js:',3)) + $options[$func]='js:'.$this->$func; + } + + return $options; + } +} diff --git a/web/framework-1.0.8/web/widgets/CClipWidget.php b/web/framework-1.0.8/web/widgets/CClipWidget.php new file mode 100644 index 0000000..d91635a --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CClipWidget.php @@ -0,0 +1,55 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CClipWidget records its content and makes it available elsewhere. + * + * Content rendered between its {@link init()} and {@link run()} calls are saved + * as a clip in the controller. The clip is named after the widget ID. + * + * See {@link CBaseController::beginClip} and {@link CBaseController::endClip} + * for a shortcut usage of CClipWidget. + * + * @author Qiang Xue + * @version $Id: CClipWidget.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CClipWidget extends CWidget +{ + /** + * @var boolean whether to render the clip content in place. Defaults to false, + * meaning the captured clip will not be displayed. + */ + public $renderClip=false; + + private $_level; + + /** + * Starts recording a clip. + */ + public function init() + { + ob_start(); + ob_implicit_flush(false); + } + + /** + * Ends recording a clip. + * This method stops output buffering and saves the rendering result as a named clip in the controller. + */ + public function run() + { + $clip=ob_get_clean(); + if($this->renderClip) + echo $clip; + $this->getController()->getClips()->add($this->getId(),$clip); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CContentDecorator.php b/web/framework-1.0.8/web/widgets/CContentDecorator.php new file mode 100644 index 0000000..5b1cd3c --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CContentDecorator.php @@ -0,0 +1,83 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CContentDecorator decorates the content it encloses with the specified view. + * + * CContentDecorator is mostly used to implement nested layouts, i.e., a layout + * is embedded within another layout. {@link CBaseController} defines a pair of + * convenient methods to use CContentDecorator: + *
    + * $this->beginContent('path/to/view');
    + * // ... content to be decorated
    + * $this->endContent();
    + * 
    + * + * The property {@link view} specifies the name of the view that is used to + * decorate the content. In the view, the content being decorated may be + * accessed with variable $content. + * + * @author Qiang Xue + * @version $Id: CContentDecorator.php 864 2009-03-21 17:23:33Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CContentDecorator extends COutputProcessor +{ + /** + * @var mixed the name of the view that will be used to decorate the captured content. + * If this property is null (default value), the default layout will be used as + * the decorative view. Note that if the current controller does not belong to + * any module, the default layout refers to the application's {@link CWebApplication::layout default layout}; + * If the controller belongs to a module, the default layout refers to the module's + * {@link CWebModule::layout default layout}. + */ + public $view; + /** + * @var array the variables (name=>value) to be extracted and made available in the decorative view. + * @since 1.0.4 + */ + public $data=array(); + + /** + * Processes the captured output. + * This method decorates the output with the specified {@link view}. + * @param string the captured output to be processed + */ + public function processOutput($output) + { + $output=$this->decorate($output); + parent::processOutput($output); + } + + /** + * Decorates the content by rendering a view and embedding the content in it. + * The content being embedded can be accessed in the view using variable $content + * The decorated content will be displayed directly. + * @param string the content to be decorated + * @return string the decorated content + */ + protected function decorate($content) + { + $owner=$this->getOwner(); + if($this->view===null) + $viewFile=Yii::app()->getController()->getLayoutFile(null); + else + $viewFile=$owner->getViewFile($this->view); + if($viewFile!==false) + { + $data=$this->data; + $data['content']=$content; + return $owner->renderFile($viewFile,$data,true); + } + else + return $content; + } +} diff --git a/web/framework-1.0.8/web/widgets/CFilterWidget.php b/web/framework-1.0.8/web/widgets/CFilterWidget.php new file mode 100644 index 0000000..51523ac --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CFilterWidget.php @@ -0,0 +1,74 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFilterWidget is the base class for widgets that can also be used as filters. + * + * Derived classes may need to override the following methods: + *
      + *
    • {@link CWidget::init()} : called when this is object is used as a widget and needs initialization.
    • + *
    • {@link CWidget::run()} : called when this is object is used as a widget.
    • + *
    • {@link filter()} : the filtering method called when this object is used as an action filter.
    • + *
    + * + * CFilterWidget provides all properties and methods of {@link CWidget} and {@link CFilter}. + * + * @author Qiang Xue + * @version $Id: CFilterWidget.php 916 2009-04-04 21:54:27Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CFilterWidget extends CWidget implements IFilter +{ + /** + * @var boolean whether to stop the action execution when this widget is used as a filter. + * This property should be changed only in {@link CWidget::init} method. + * Defaults to false, meaning the action should be executed. + */ + public $stopAction=false; + + private $_isFilter; + + /** + * Constructor. + * @param CBaseController owner/creator of this widget. It could be either a widget or a controller. + */ + public function __construct($owner=null) + { + parent::__construct($owner); + $this->_isFilter=($owner===null); + } + + /** + * @return boolean whether this widget is used as a filter. + * @since 1.0.4 + */ + public function getIsFilter() + { + return $this->_isFilter; + } + + /** + * Performs the filtering. + * The default implementation simply calls {@link init()}, + * {@link CFilterChain::run()} and {@link run()} in order + * Derived classes may want to override this method to change this behavior. + * @param CFilterChain the filter chain that the filter is on. + */ + public function filter($filterChain) + { + $this->init(); + if(!$this->stopAction) + { + $filterChain->run(); + $this->run(); + } + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CFlexWidget.php b/web/framework-1.0.8/web/widgets/CFlexWidget.php new file mode 100644 index 0000000..c21b5e5 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CFlexWidget.php @@ -0,0 +1,116 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFlexWidget embeds a Flex 3.x application into a page. + * + * To use CFlexWidget, set {@link name} to be the Flex application name + * (without the .swf suffix), and set {@link baseUrl} to be URL (without the ending slash) + * of the directory containing the SWF file of the Flex application. + * + * @author Qiang Xue + * @version $Id: CFlexWidget.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CFlexWidget extends CWidget +{ + /** + * @var string name of the Flex application. + * This should be the SWF file name without the ".swf" suffix. + */ + public $name; + /** + * @var string the base URL of the Flex application. + * This refers to the URL of the directory containing the SWF file. + */ + public $baseUrl; + /** + * @var string width of the application region. Defaults to 450. + */ + public $width='100%'; + /** + * @var string height of the application region. Defaults to 300. + */ + public $height='100%'; + /** + * @var string quality of the animation. Defaults to 'high'. + */ + public $quality='high'; + /** + * @var string background color of the application region. Defaults to '#FFFFFF', meaning white. + */ + public $bgColor='#FFFFFF'; + /** + * @var string align of the application region. Defaults to 'middle'. + */ + public $align='middle'; + /** + * @var string the access method of the script. Defaults to 'sameDomain'. + */ + public $allowScriptAccess='sameDomain'; + /** + * @var string the HTML content to be displayed if Flash player is not installed. + */ + public $altHtmlContent; + /** + * @var boolean whether history should be enabled. Defaults to true. + */ + public $enableHistory=true; + /** + * @var array parameters to be passed to the Flex application. + */ + public $flashVars=array(); + + /** + * Renders the widget. + */ + public function run() + { + if(empty($this->name)) + throw new CException(Yii::t('yii','CFlexWidget.name cannot be empty.')); + if(empty($this->baseUrl)) + throw new CException(Yii::t('yii','CFlexWidget.baseUrl cannot be empty.')); + if($this->altHtmlContent===null) + $this->altHtmlContent=Yii::t('yii','This content requires the Adobe Flash Player.'); + + $this->registerClientScript(); + + $this->render('flexWidget'); + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + $cs=Yii::app()->getClientScript(); + $cs->registerScriptFile($this->baseUrl.'/AC_OETags.js'); + + if($this->enableHistory) + { + $cs->registerCssFile($this->baseUrl.'/history/history.css'); + $cs->registerScriptFile($this->baseUrl.'/history/history.js'); + } + } + + /** + * Generates the properly quoted flash parameter string. + * @return string the flash parameter string. + */ + public function getFlashVarsAsString() + { + $params=array(); + foreach($this->flashVars as $k=>$v) + $params[]=urlencode($k).'='.urlencode($v); + return CJavaScript::quote(implode('&',$params)); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CHtmlPurifier.php b/web/framework-1.0.8/web/widgets/CHtmlPurifier.php new file mode 100644 index 0000000..384de6d --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CHtmlPurifier.php @@ -0,0 +1,64 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +require_once(Yii::getPathOfAlias('system.vendors.htmlpurifier').DIRECTORY_SEPARATOR.'HTMLPurifier.standalone.php'); +HTMLPurifier_Bootstrap::registerAutoload(); + +/** + * CHtmlPurifier is wrapper of {@link http://htmlpurifier.org HTML Purifier}. + * + * CHtmlPurifier removes all malicious code (better known as XSS) with a thoroughly audited, + * secure yet permissive whitelist. It will also make sure the resulting code + * is standard-compliant. + * + * CHtmlPurifier can be used as either a widget or a controller filter. + * + * Note: since HTML Purifier is a big package, its performance is not very good. + * You should consider either caching the purification result or purifying the user input + * before saving to database. + * + * @author Qiang Xue + * @version $Id: CHtmlPurifier.php 1074 2009-05-28 21:34:21Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CHtmlPurifier extends COutputProcessor +{ + /** + * @var mixed the options to be passed to {@link http://htmlpurifier.org HTML Purifier}. + * This can be a HTMLPurifier_Config object, an array of directives (Namespace.Directive => Value) + * or the filename of an ini file. + * @see http://htmlpurifier.org/live/configdoc/plain.html + */ + public $options=null; + + /** + * Processes the captured output. + * This method purifies the output using {@link http://htmlpurifier.org HTML Purifier}. + * @param string the captured output to be processed + */ + public function processOutput($output) + { + $output=$this->purify($output); + parent::processOutput($output); + } + + /** + * Purifies the HTML content by removing malicious code. + * @param string the content to be purified. + * @return string the purified content + */ + public function purify($content) + { + $purifier=new HTMLPurifier($this->options); + $purifier->config->set('Cache','SerializerPath',Yii::app()->getRuntimePath()); + return $purifier->purify($content); + } +} diff --git a/web/framework-1.0.8/web/widgets/CInputWidget.php b/web/framework-1.0.8/web/widgets/CInputWidget.php new file mode 100644 index 0000000..0c686f6 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CInputWidget.php @@ -0,0 +1,80 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CInputWidget is the base class for widgets that collect user inputs. + * + * CInputWidget declares properties common among input widgets. An input widget + * can be associated with a data model and an attribute, or a name and a value. + * If the former, the name and the value will be generated automatically. + * Child classes may use {@link resolveNameID} and {@link hasModel}. + * + * @author Qiang Xue + * @version $Id: CInputWidget.php 960 2009-04-27 17:49:01Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +abstract class CInputWidget extends CWidget +{ + /** + * @var CModel the data model associated with this widget. + */ + public $model; + /** + * @var string the attribute associated with this widget. + */ + public $attribute; + /** + * @var string the input name. This must be set if {@link model} is not set. + */ + public $name; + /** + * @var string the input value + */ + public $value; + /** + * @var array additional HTML options to be rendered in the input tag + */ + public $htmlOptions=array(); + + + /** + * @return array the name and the ID of the input. + */ + protected function resolveNameID() + { + if($this->name!==null) + $name=$this->name; + else if($this->hasModel()) + $name=CHtml::activeName($this->model,$this->attribute); + else if(isset($this->htmlOptions['name'])) + $name=$this->htmlOptions['name']; + else + throw new CException(Yii::t('yii','{class} must specify "model" and "attribute" or "name" property values.',array('{class}'=>get_class($this)))); + + if(($id=$this->getId(false))===null) + { + if(isset($this->htmlOptions['id'])) + $id=$this->htmlOptions['id']; + else + $id=CHtml::getIdByName($name); + } + + return array($name,$id); + } + + /** + * @return boolean whether this widget is associated with a data model. + */ + protected function hasModel() + { + return $this->model instanceof CModel && $this->attribute!==null; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CMarkdown.php b/web/framework-1.0.8/web/widgets/CMarkdown.php new file mode 100644 index 0000000..66db4c7 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CMarkdown.php @@ -0,0 +1,119 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMarkdown converts the captured content from markdown syntax to HTML code. + * + * CMarkdown can be used as either a widget or a filter. It is a wrapper of {@link CMarkdownParser}. + * CMarkdown adds an additional option {@link purifyOutput} which can be set true + * so that the converted HTML code is purified before being displayed. + * + * For details about the markdown syntax, please check the following: + *
      + *
    • {@link http://daringfireball.net/projects/markdown/syntax official markdown syntax}
    • + *
    • {@link http://michelf.com/projects/php-markdown/extra/ markdown extra syntax}
    • + *
    • {@link CMarkdownParser markdown with syntax highlighting}
    • + *
    + * + * @author Qiang Xue + * @version $Id: CMarkdown.php 483 2009-01-07 03:39:08Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CMarkdown extends COutputProcessor +{ + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var boolean whether to use {@link CHtmlPurifier} to purify the generated HTML code. Defaults to false. + */ + public $purifyOutput=false; + + private $_parser; + + /** + * Processes the captured output. + * This method converts the content in markdown syntax to HTML code. + * If {@link purifyOutput} is true, the HTML code will also be purified. + * @param string the captured output to be processed + * @see convert + */ + public function processOutput($output) + { + $output=$this->transform($output); + if($this->purifyOutput) + { + $purifier=new CHtmlPurifier; + $output=$purifier->purify($output); + } + parent::processOutput($output); + } + + /** + * Converts the content in markdown syntax to HTML code. + * This method uses {@link CMarkdownParser} to do the conversion. + * @param string the content to be converted + * @return string the converted content + */ + public function transform($output) + { + $this->registerClientScript(); + return $this->getMarkdownParser()->transform($output); + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + CTextHighlighter::registerCssFile($url); + } + + /** + * Returns the markdown parser instance. + * This method calls {@link createMarkdownParser} to create the parser instance. + * Call this method multipe times will only return the same instance. + * @param CMarkdownParser the parser instance + * @since 1.0.1 + */ + public function getMarkdownParser() + { + if($this->_parser===null) + $this->_parser=$this->createMarkdownParser(); + return $this->_parser; + } + + /** + * Creates a markdown parser. + * By default, this method creates a {@link CMarkdownParser} instance. + * @return CMarkdownParser the markdown parser. + */ + protected function createMarkdownParser() + { + return new CMarkdownParser; + } +} diff --git a/web/framework-1.0.8/web/widgets/CMaskedTextField.php b/web/framework-1.0.8/web/widgets/CMaskedTextField.php new file mode 100644 index 0000000..8f55a28 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CMaskedTextField.php @@ -0,0 +1,106 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMaskedTextField generates a masked text field. + * + * CMaskedTextField is similar to {@link CHtml::textField} except that + * an input mask will be used to help users enter properly formatted data. + * The masked text field is implemented based on the jQuery masked input plugin + * (see {@link http://digitalbush.com/projects/masked-input-plugin}). + * + * @author Qiang Xue + * @version $Id: CMaskedTextField.php 949 2009-04-19 03:39:06Z qiang.xue@gmail.com $ + * @package system.web.widgets + * @since 1.0 + */ +class CMaskedTextField extends CInputWidget +{ + /** + * @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined: + *
      + *
    • a: represents an alpha character (A-Z,a-z)
    • + *
    • 9: represents a numeric character (0-9)
    • + *
    • *: represents an alphanumeric character (A-Z,a-z,0-9).
    • + *
    + * Additional characters can be defined by specifying the {@link charMap} property. + */ + public $mask; + /** + * @var array the mapping between mask characters and the corresponding patterns. + * For example, array('~'=>'[+-]') specifies that the '~' character expects '+' or '-' input. + * Defaults to null, meaning using the map as described in {@link mask}. + */ + public $charMap; + /** + * @var string the character prompting for user input. Defaults to underscore '_'. + */ + public $placeholder; + /** + * @var string a JavaScript function callback that will be invoked when user finishes the input. + */ + public $completed; + + /** + * Executes the widget. + * This method registers all needed client scripts and renders + * the text field. + */ + public function run() + { + list($name,$id)=$this->resolveNameID(); + $this->htmlOptions['id']=$id; + $this->htmlOptions['name']=$name; + + $this->registerClientScript(); + + if($this->hasModel()) + echo CHtml::activeTextField($this->model,$this->attribute,$this->htmlOptions); + else + echo CHtml::textField($name,$this->value,$this->htmlOptions); + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + $id=$this->htmlOptions['id']; + $miOptions=$this->getClientOptions(); + $options=$miOptions!==array() ? ','.CJavaScript::encode($miOptions) : ''; + $js=''; + if(is_array($this->charMap)) + $js.='jQuery.mask.definitions='.CJavaScript::encode($this->charMap).";\n"; + $js.="jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});"; + + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('maskedinput'); + $cs->registerScript('Yii.CMaskedTextField#'.$id,$js); + } + + /** + * @return array the options for the text field + */ + protected function getClientOptions() + { + $options=array(); + if($this->placeholder!==null) + $options['placeholder']=$this->placeholder; + if(is_string($this->completed)) + { + if(strncmp($this->completed,'js:',3)) + $options['completed']='js:'.$this->completed; + else + $options['completed']=$this->completed; + } + return $options; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CMultiFileUpload.php b/web/framework-1.0.8/web/widgets/CMultiFileUpload.php new file mode 100644 index 0000000..6a6533e --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CMultiFileUpload.php @@ -0,0 +1,115 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CMultiFileUpload generates a file input that can allow uploading multiple files at a time. + * + * This is based on the {@link http://www.fyneworks.com/jquery/multiple-file-upload/ jQuery Multi File Upload plugin}. + * The uploaded file information can be accessed via $_FILES[widget-name], which gives an array of the uploaded + * files. Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'. + * + * @author Qiang Xue + * @version $Id: CMultiFileUpload.php 970 2009-04-30 02:59:02Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CMultiFileUpload extends CWidget +{ + /** + * @var string the input name. + */ + public $name; + /** + * @var string the file types that are allowed (e.g. "gif|jpg"). Note, the server side still + * needs to check if the uploaded files have allowed types. + */ + public $accept; + /** + * @var integer the maximum number of files that can be uploaded. If -1, it means no limits. Defaults to -1. + */ + public $max=-1; + /** + * @var string the label for the remove button. Defaults to "Remove". + */ + public $remove; + /** + * @var string message that is displayed when a file type is not allowed. + */ + public $denied; + /** + * @var string message that is displayed when a file is selected. + */ + public $selected; + /** + * @var string message that is displayed when a file appears twice. + */ + public $duplicate; + /** + * @var array additional HTML attributes that will be rendered in the file upload tag. + */ + public $htmlOptions=array(); + + + /** + * Runs the widget. + * This method registers all needed client scripts and renders + * the multiple file uploader. + */ + public function run() + { + if($this->name!==null) + $name=$this->name; + else if(isset($this->htmlOptions['name'])) + $name=$this->htmlOptions['name']; + else + throw new CException(Yii::t('yii','CMultiFileUpload.name is required.')); + if(substr($name,-2)!=='[]') + $name.='[]'; + if(($id=$this->getId(false))===null) + { + if(isset($this->htmlOptions['id'])) + $id=$this->htmlOptions['id']; + else + $id=CHtml::getIdByName($name); + } + $this->htmlOptions['id']=$id; + + $this->registerClientScript(); + + echo CHtml::fileField($name,'',$this->htmlOptions); + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + $id=$this->htmlOptions['id']; + $mfOptions=array(); + if($this->accept!==null) + $mfOptions['accept']=$this->accept; + if($this->max>0) + $mfOptions['max']=$this->max; + $messages=array(); + foreach(array('remove','denied','selected','duplicate') as $messageName) + { + if($this->$messageName!==null) + $messages[$messageName]=$this->$messageName; + } + if($messages!==array()) + $mfOptions['STRING']=$messages; + $options=$mfOptions===array()?'':CJavaScript::encode($mfOptions); + + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('multifile'); + $cs->registerScript('Yii.CMultiFileUpload#'.$id,"jQuery(\"#{$id}\").MultiFile({$options});"); + } +} diff --git a/web/framework-1.0.8/web/widgets/COutputCache.php b/web/framework-1.0.8/web/widgets/COutputCache.php new file mode 100644 index 0000000..ea18d08 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/COutputCache.php @@ -0,0 +1,336 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COutputCache enables caching the output generated by an action or a view fragment. + * + * If the output to be displayed is found valid in cache, the cached + * version will be displayed instead, which saves the time for generating + * the original output. + * + * Since COutputCache extends from {@link CFilterWidget}, it can be used + * as either a filter (for action caching) or a widget (for fragment caching). + * For the latter, the shortcuts {@link CBaseController::beginCache()} and {@link CBaseController::endCache()} + * are often used instead, like the following in a view file: + *
    + * if($this->beginCache('cacheName',array('property1'=>'value1',...))
    + * {
    + *     // ... display the content to be cached here
    + *    $this->endCache();
    + * }
    + * 
    + * + * COutputCache must work with a cache application component specified via {@link cacheID}. + * If the cache application component is not available, COutputCache will be disabled. + * + * The validity of the cached content is determined based on two factors: + * the {@link duration} and the cache {@link dependency}. + * The former specifies the number of seconds that the data can remain + * valid in cache (defaults to 60s), while the latter specifies conditions + * that the cached data depends on. If a dependency changes, + * (e.g. relevant data in DB are updated), the cached data will be invalidated. + * For more details about cache dependency, see {@link CCacheDependency}. + * + * Sometimes, it is necessary to turn off output caching only for certain request types. + * For exapmle, we only want to cache a form when it is initially requested; + * any subsequent display of the form should not be cached because it contains user input. + * We can set {@link requestTypes} to be array('GET') to accomplish this task. + * + * The content fetched from cache may be variated with respect to + * some parameters. COutputCache supports two kinds of variations: + *
      + *
    • {@link varyByRoute}: this specifies whether the cached content + * should be varied with the requested route (controller and action)
    • + *
    • {@link varyByParam}: this specifies a list of GET parameter names + * and uses the corresponding values to determine the version of the cached content.
    • + *
    • {@link varyBySession}: this specifies whether the cached content + * should be varied with the user session.
    • + *
    • {@link varyByExpression}: this specifies whether the cached content + * should be varied with the result of the specified PHP expression.
    • + *
    + * For more advanced variation, override {@link getBaseCacheKey()} method. + * + * @author Qiang Xue + * @version $Id: COutputCache.php 1087 2009-06-03 17:56:23Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class COutputCache extends CFilterWidget +{ + /** + * Prefix to the keys for storing cached data + */ + const CACHE_KEY_PREFIX='Yii.COutputCache.'; + + /** + * @var integer number of seconds that the data can remain in cache. Defaults to 60 seconds. + * If 0 or negative, it means the cache is disabled. Note, if cache dependency changes or cache space is limited, + * the data may be purged out of cache earlier. + */ + public $duration=60; + /** + * @var boolean whether the content being cached should be differentiated according to route. + * A route consists of the requested controller ID and action ID. + * Defaults to true. + */ + public $varyByRoute=true; + /** + * @var boolean whether the content being cached should be differentiated according to user sessions. Defaults to false. + */ + public $varyBySession=false; + /** + * @var array list of GET parameters that should participate in cache key calculation. + * By setting this property, the output cache will use different cached data + * for each different set of GET parameter values. + */ + public $varyByParam; + /** + * @var string a PHP expression whose result is used in the cache key calculation. + * By setting this property, the output cache will use different cached data + * for each different expression result. + * @since 1.0.4 + */ + public $varyByExpression; + /** + * @var array list of request types (e.g. GET, POST) for which the cache should be enabled only. + * Defaults to null, meaning all request types. + */ + public $requestTypes; + /** + * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.) + */ + public $cacheID='cache'; + /** + * @var mixed the dependency that the cached content depends on. + * This can be either an object implementing {@link ICacheDependency} interface or an array + * specifying the configuration of the dependency object. For example, + *
    +	 * array(
    +	 *     'class'=>'CDbCacheDependency',
    +	 *     'sql'=>'SELECT MAX(lastModified) FROM Post',
    +	 * )
    +	 * 
    + * would make the output cache depends on the last modified time of all posts. + * If any post has its modification time changed, the cached content would be invalidated. + */ + public $dependency; + + private $_key; + private $_cache; + private $_contentCached; + private $_content; + private $_actions; + + /** + * Performs filtering before the action is executed. + * This method is meant to be overridden by child classes if begin-filtering is needed. + * @return boolean whether the filtering process should stop after this filter. Defaults to false. + */ + public function filter($filterChain) + { + $this->init(); + if(!$this->getIsContentCached()) + $filterChain->run(); + $this->run(); + } + + /** + * Marks the start of content to be cached. + * Content displayed after this method call and before {@link endCache()} + * will be captured and saved in cache. + * This method does nothing if valid content is already found in cache. + */ + public function init() + { + if($this->getIsContentCached()) + $this->replayActions(); + else if($this->_cache!==null) + { + $this->getController()->getCachingStack()->push($this); + ob_start(); + ob_implicit_flush(false); + } + } + + /** + * Marks the end of content to be cached. + * Content displayed before this method call and after {@link init()} + * will be captured and saved in cache. + * This method does nothing if valid content is already found in cache. + */ + public function run() + { + if($this->getIsContentCached()) + { + if($this->getController()->isCachingStackEmpty()) + echo $this->getController()->processDynamicOutput($this->_content); + else + echo $this->_content; + } + else if($this->_cache!==null) + { + $this->_content=ob_get_clean(); + $this->getController()->getCachingStack()->pop(); + $data=array($this->_content,$this->_actions); + if(is_array($this->dependency)) + $this->dependency=Yii::createComponent($this->dependency); + $this->_cache->set($this->getCacheKey(),$data,$this->duration>0 ? $this->duration : 0,$this->dependency); + + if($this->getController()->isCachingStackEmpty()) + echo $this->getController()->processDynamicOutput($this->_content); + else + echo $this->_content; + } + } + + /** + * @return boolean whether the content can be found from cache + */ + public function getIsContentCached() + { + if($this->_contentCached!==null) + return $this->_contentCached; + else + return $this->_contentCached=$this->checkContentCache(); + } + + /** + * Looks for content in cache. + * @return boolean whether the content is found in cache. + */ + protected function checkContentCache() + { + if((empty($this->requestTypes) || in_array(Yii::app()->getRequest()->getRequestType(),$this->requestTypes)) + && $this->duration>0 && ($this->_cache=$this->getCache())!==null) + { + if(($data=$this->_cache->get($this->getCacheKey()))!==false) + { + $this->_content=$data[0]; + $this->_actions=$data[1]; + return true; + } + } + return false; + } + + /** + * @return ICache the cache used for caching the content. + */ + protected function getCache() + { + return Yii::app()->getComponent($this->cacheID); + } + + /** + * Caclulates the base cache key. + * The calculated key will be further variated in {@link getCacheKey}. + * Derived classes may override this method if more variations are needed. + * @return string basic cache key without variations + */ + protected function getBaseCacheKey() + { + return self::CACHE_KEY_PREFIX.$this->getId().'.'; + } + + /** + * Calculates the cache key. + * The key is calculated based on {@link getBaseCacheKey} and other factors, including + * {@link varyByRoute}, {@link varyByParam} and {@link varyBySession}. + * @return string cache key + */ + protected function getCacheKey() + { + if($this->_key!==null) + return $this->_key; + else + { + $key=$this->getBaseCacheKey().'.'; + if($this->varyByRoute) + { + $controller=$this->getController(); + $key.=$controller->getUniqueId().'/'; + if(($action=$controller->getAction())!==null) + $key.=$action->getId(); + } + $key.='.'; + + if($this->varyBySession) + $key.=Yii::app()->getSession()->getSessionID(); + $key.='.'; + + if(is_array($this->varyByParam) && isset($this->varyByParam[0])) + { + $params=array(); + foreach($this->varyByParam as $name) + { + if(isset($_GET[$name])) + $params[$name]=$_GET[$name]; + else + $params[$name]=''; + } + $key.=serialize($params); + } + $key.='.'; + + if($this->varyByExpression!==null) + $key.=$this->evaluateExpression($this->varyByExpression); + $key.='.'; + + return $this->_key=$key; + } + } + + private function evaluateExpression($expression) + { + return @eval('return '.$expression.';'); + } + + /** + * Records a method call when this output cache is in effect. + * When the content is served from the output cache, the recorded + * method will be re-invoked. + * @param string a property name of the controller. The property should refer to an object + * whose method is being recorded. If empty it means the controller itself. + * @param string the method name + * @param array parameters passed to the method + */ + public function recordAction($context,$method,$params) + { + $this->_actions[]=array($context,$method,$params); + } + + /** + * Replays the recorded method calls. + */ + protected function replayActions() + { + if(empty($this->_actions)) + return; + $controller=$this->getController(); + $cs=Yii::app()->getClientScript(); + foreach($this->_actions as $action) + { + if($action[0]==='clientScript') + $object=$cs; + else if($action[0]==='') + $object=$controller; + else + $object=$controller->{$action[0]}; + if(method_exists($object,$action[1])) + call_user_func_array(array($object,$action[1]),$action[2]); + else if($action[0]==='' && function_exists($action[1])) + call_user_func_array($action[1],$action[2]); + else + throw new CException(Yii::t('yii','Unable to replay the action "{object}.{method}". The method does not exist.', + array('object'=>$action[0], + 'method'=>$action[1]))); + } + } +} diff --git a/web/framework-1.0.8/web/widgets/COutputProcessor.php b/web/framework-1.0.8/web/widgets/COutputProcessor.php new file mode 100644 index 0000000..4ad5478 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/COutputProcessor.php @@ -0,0 +1,77 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * COutputProcessor transforms the content into a different format. + * + * COutputProcessor captures the output generated by an action or a view fragment + * and passes it to its {@link onProcessOutput} event handlers for further processing. + * + * The event handler may process the output and store it back to the {@link COutputEvent::output} + * property. By setting the {@link CEvent::handled handled} property of the event parameter + * to true, the output will not be echoed anymore. Otherwise (by default), the output will be echoed. + * + * @author Qiang Xue + * @version $Id: COutputProcessor.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class COutputProcessor extends CFilterWidget +{ + /** + * Initializes the widget. + * This method starts the output buffering. + */ + public function init() + { + ob_start(); + ob_implicit_flush(false); + } + + /** + * Executes the widget. + * This method stops output buffering and processes the captured output. + */ + public function run() + { + $output=ob_get_clean(); + $this->processOutput($output); + } + + /** + * Processes the captured output. + * + * The default implementation raises an {@link onProcessOutput} event. + * If the event is not handled by any event handler, the output will be echoed. + * + * @param string the captured output to be processed + */ + public function processOutput($output) + { + if($this->hasEventHandler('onProcessOutput')) + { + $event=new COutputEvent($this,$output); + $this->onProcessOutput($event); + if(!$event->handled) + echo $output; + } + else + echo $output; + } + + /** + * Raised when the output has been captured. + * @param COutputEvent event parameter + */ + public function onProcessOutput($event) + { + $this->raiseEvent('onProcessOutput',$event); + } +} diff --git a/web/framework-1.0.8/web/widgets/CStarRating.php b/web/framework-1.0.8/web/widgets/CStarRating.php new file mode 100644 index 0000000..1e7add9 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CStarRating.php @@ -0,0 +1,207 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CStarRating displays a star rating control that can collect user rating input. + * + * CStarRating is based on {@link http://www.fyneworks.com/jquery/star-rating/ jQuery Star Rating Plugin}. + * It displays a list of stars indicating the rating values. Users can toggle these stars + * to indicate their rating input. On the server side, when the rating input is submitted, + * the value can be retrieved in the same way as working with a normal HTML input. + * For example, using + *
    + * $this->widget('CStarRating',array('name'=>'rating'));
    + * 
    + * we can retrieve the rating value via $_POST['rating']. + * + * CStarRating allows customization of its appearance. It also supports empty rating as well as read-only rating. + * + * @author Qiang Xue + * @version $Id: CStarRating.php 913 2009-04-04 02:01:48Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CStarRating extends CInputWidget +{ + /** + * @var integer the number of stars. Defaults to 5. + */ + public $starCount=5; + /** + * @var mixed the minimum rating allowed. This can be either an integer or a double value. Defaults to 1. + */ + public $minRating=1; + /** + * @var mixed the maximum rating allowed. This can be either an integer or a double value. Defaults to 1. + */ + public $maxRating=10; + /** + * @var mixed the step size of rating. This is the minimum difference between two rating values. Defaults to 1. + */ + public $ratingStepSize=1; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var array the titles associated with the rating options. The keys are ratings and the values are the corresponding titles. + * Defaults to null, meaning using the rating value as the title. + */ + public $titles; + /** + * @var string the hint text for the reset button. Defaults to null, meaning using the system-defined text (which is 'Cancel Rating'). + */ + public $resetText; + /** + * @var string the value taken when the rating is cleared. Defaults to null, meaning using the system-defined value (which is ''). + */ + public $resetValue; + /** + * @var boolean whether the rating value can be empty (not set). Defaults to true. + * When this is true, a reset button will be displayed in front of stars. + */ + public $allowEmpty; + /** + * @var integer the width of star image. Defaults to null, meaning using the system-defined value (which is 16). + */ + public $starWidth; + /** + * @var boolean whether the rating value is read-only or not. Defaults to false. + * When this is true, the rating cannot be changed. + */ + public $readOnly; + /** + * @var string Callback when the stars are focused. + */ + public $focus; + /** + * @var string Callback when the stars are not focused. + */ + public $blur; + /** + * @var string Callback when the stars are clicked. + */ + public $callback; + + + /** + * Executes the widget. + * This method registers all needed client scripts and renders + * the text field. + */ + public function run() + { + list($name,$id)=$this->resolveNameID(); + $this->htmlOptions['id']=$id; + $this->htmlOptions['name']=$name; + + $this->registerClientScript($id); + + echo CHtml::openTag('span',$this->htmlOptions)."\n"; + $this->renderStars($id,$name); + echo ""; + } + + /** + * Registers the necessary javascript and css scripts. + * @param string the ID of the container + */ + public function registerClientScript($id) + { + $jsOptions=$this->getClientOptions(); + $jsOptions=empty($jsOptions) ? '' : CJavaScript::encode($jsOptions); + $js="jQuery('#{$id} > input').rating({$jsOptions});"; + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('rating'); + $cs->registerScript('Yii.CStarRating#'.$id,$js); + + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + $cs=Yii::app()->getClientScript(); + if($url===null) + $url=$cs->getCoreScriptUrl().'/rating/jquery.rating.css'; + $cs->registerCssFile($url); + } + + /** + * Renders the stars. + * @param string the ID of the container + * @param string the name of the input + */ + protected function renderStars($id,$name) + { + $inputCount=(int)(($this->maxRating-$this->minRating)/$this->ratingStepSize+1); + $starSplit=(int)($inputCount/$this->starCount); + $selection=$this->hasModel() ? $this->model->{$this->attribute} : $this->value; + $options=$starSplit>1 ? array('class'=>"{split:{$starSplit}}") : array(); + for($value=$this->minRating, $i=0;$i<$inputCount; ++$i, $value+=$this->ratingStepSize) + { + $options['id']=$id.'_'.$i; + $options['value']=$value; + if(isset($this->titles[$value])) + $options['title']=$this->titles[$value]; + else + unset($options['title']); + echo CHtml::radioButton($name,!strcmp($value,$selection),$options) . "\n"; + } + } + + /** + * @return array the javascript options for the star rating + */ + protected function getClientOptions() + { + $options=array(); + if($this->resetText!==null) + $options['cancel']=$this->resetText; + if($this->resetValue!==null) + $options['cancelValue']=$this->resetValue; + if($this->allowEmpty===false) + $options['required']=true; + if($this->starWidth!==null) + $options['starWidth']=$this->starWidth; + if($this->readOnly===true) + $options['readOnly']=true; + if($this->focus!==null) + { + if(strncmp($this->focus,'js:',3)) + $options['focus']='js:'.$this->focus; + else + $options['focus']=$this->focus; + } + if($this->blur!==null) + { + if(strncmp($this->blur,'js:',3)) + $options['blur']='js:'.$this->blur; + else + $options['blur']=$this->blur; + } + if($this->callback!==null) + { + if(strncmp($this->callback,'js:',3)) + $options['callback']='js:'.$this->callback; + else + $options['callback']=$this->callback; + } + return $options; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/CTabView.php b/web/framework-1.0.8/web/widgets/CTabView.php new file mode 100644 index 0000000..2d4d4d0 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CTabView.php @@ -0,0 +1,201 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTabView displays contents in multiple tabs. + * + * At any time, only one tab is visible. Users can click on the tab header + * to switch to see another tab of content. + * + * JavaScript is used to control the tab switching. If JavaScript is disabled, + * CTabView still manages to display the content in a semantically appropriate way. + * + * To specify contents and their tab structure, configure the {@link tabs} property. + * The {@link tabs} property takes an array with tab ID being mapped tab definition. + * Each tab definition is an array of the following structure: + *
      + *
    • title: the tab title.
    • + *
    • content: the content to be displayed in the tab.
    • + *
    • view: the name of the view to be displayed in this tab. + * The view will be rendered using the current controller's + * {@link CController::renderPartial} method. + * When both 'content' and 'view' are specified, 'content' will take precedence. + *
    • + *
    • url: a URL that the user browser will be redirected to when clicking on this tab.
    • + *
    + * + * For example, the {@link tabs} property can be configured as follows, + *
    + * array(
    + *     'tab1'=>array(
    + *           'title'=>'tab 1 title',
    + *           'view'=>'view1',
    + *     ),
    + *     'tab2'=>array(
    + *           'title'=>'tab 2 title',
    + *           'url'=>'http://www.yiiframework.com/',
    + *     ),
    + * )
    + * 
    + * + * By default, the first tab will be activated. To activate a different tab + * when the page is initially loaded, set {@link activeTab} to be the ID of the desired tab. + * + * @author Qiang Xue + * @version $Id: CTabView.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CTabView extends CWidget +{ + /** + * Default CSS class for the tab container + */ + const CSS_CLASS='yiiTab'; + + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var string the ID of the tab that should be activated when the page is initially loaded. + * If not set, the first tab will be activated. + */ + public $activeTab; + /** + * @var array the data that will be passed to the partial view rendered by each tab. + */ + public $viewData; + /** + * @var array additional HTML options to be rendered in the container tag. + */ + public $htmlOptions; + /** + * @var array tab definitions. The array keys are the IDs, + * and the array values are the corresponding tab contents. + * Each array value must be an array with the following elements: + *
      + *
    • title: the tab title. You need to make sure this is HTML-encoded.
    • + *
    • content: the content to be displayed in the tab.
    • + *
    • view: the name of the view to be displayed in this tab. + * The view will be rendered using the current controller's + * {@link CController::renderPartial} method. + * When both 'content' and 'view' are specified, 'content' will take precedence. + *
    • + *
    • url: a URL that the user browser will be redirected to when clicking on this tab.
    • + *
    + *
    +	 * array(
    +	 *     'tab1'=>array(
    +	 *           'title'=>'tab 1 title',
    +	 *           'view'=>'view1',
    +	 *     ),
    +	 *     'tab2'=>array(
    +	 *           'title'=>'tab 2 title',
    +	 *           'url'=>'http://www.yiiframework.com/',
    +	 *     ),
    +	 * )
    +	 * 
    + */ + public $tabs=array(); + + /** + * Runs the widget. + */ + public function run() + { + if(empty($this->tabs)) + return; + + if($this->activeTab===null || !isset($this->tabs[$this->activeTab])) + { + foreach($this->tabs as $id=>$tab) + { + $this->activeTab=$id; + break; + } + } + + $htmlOptions=$this->htmlOptions; + $htmlOptions['id']=$this->getId(); + if(!isset($htmlOptions['class'])) + $htmlOptions['class']=self::CSS_CLASS; + + $this->registerClientScript(); + + echo CHtml::openTag('div',$htmlOptions)."\n"; + $this->renderHeader(); + $this->renderBody(); + echo CHtml::closeTag('div'); + } + + /** + * Registers the needed CSS and JavaScript. + */ + public function registerClientScript() + { + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('yiitab'); + $id=$this->getId(); + $cs->registerScript('Yii.CTabView#'.$id,"jQuery(\"#{$id}\").yiitab();"); + + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + $cs=Yii::app()->getClientScript(); + if($url===null) + $url=$cs->getCoreScriptUrl().'/yiitab/jquery.yiitab.css'; + $cs->registerCssFile($url,'screen'); + } + + /** + * Renders the header part. + */ + protected function renderHeader() + { + echo "
      \n"; + foreach($this->tabs as $id=>$tab) + { + $title=isset($tab['title'])?$tab['title']:'undefined'; + $active=$id===$this->activeTab?' class="active"' : ''; + $url=isset($tab['url'])?$tab['url']:"#{$id}"; + echo "
    • {$title}
    • \n"; + } + echo "
    \n"; + } + + /** + * Renders the body part. + */ + protected function renderBody() + { + foreach($this->tabs as $id=>$tab) + { + $inactive=$id!==$this->activeTab?' style="display:none"' : ''; + echo "
    \n"; + if(isset($tab['content'])) + echo $tab['content']; + else if(isset($tab['view'])) + $this->getController()->renderPartial($tab['view'],$this->viewData); + echo "
    \n"; + } + } +} diff --git a/web/framework-1.0.8/web/widgets/CTextHighlighter.php b/web/framework-1.0.8/web/widgets/CTextHighlighter.php new file mode 100644 index 0000000..b01aa44 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CTextHighlighter.php @@ -0,0 +1,127 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter').'.php'); +require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter.Renderer.Html').'.php'); + +/** + * CTextHighlighter does syntax highlighting for its body content. + * + * The language of the syntax to be applied is specified via {@link language} property. + * Currently, CTextHighlighter supports the following languages: + * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, MYSQL, PERL, + * PHP, PYTHON, RUBY, SQL, XML. By setting {@link showLineNumbers} + * to true, the highlighted result may be shown with line numbers. + * + * @author Qiang Xue + * @version $Id: CTextHighlighter.php 483 2009-01-07 03:39:08Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CTextHighlighter extends COutputProcessor +{ + /** + * @var string the language whose syntax is to be used for highlighting. + * Valid values are those file names (without suffix) that are contained + * in 'vendors/TextHighlighter/Text/Highlighter'. Currently, the following + * languages are supported: + * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, + * MYSQL, PERL, PHP, PYTHON, RUBY, SQL, XML + * If a language is not supported, it will be displayed as plain text. + * Language names are case-insensitive. + */ + public $language; + /** + * @var boolean whether to show line numbers in the highlighted result. Defaults to false. + * @see lineNumberStyle + */ + public $showLineNumbers=false; + /** + * @var string the style of line number display. It can be either 'list' or 'table'. Defaults to 'list'. + * @see showLineNumbers + */ + public $lineNumberStyle='list'; + /** + * @var integer tab size. Defaults to 4. + */ + public $tabSize=4; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var array the HTML attributes to be applied to the container element. + * The highlighted content is contained in a DIV element. + */ + public $containerOptions=array(); + + + /** + * Processes the captured output. + * This method highlights the output according to the syntax of the specified {@link language}. + * @param string the captured output to be processed + */ + public function processOutput($output) + { + $output=$this->highlight($output); + parent::processOutput($output); + } + + /** + * Highlights the content by the syntax of the specified language. + * @param string the content to be highlighted. + * @return string the highlighted content + */ + public function highlight($content) + { + $this->registerClientScript(); + + $options['use_language']=true; + $options['tabsize']=$this->tabSize; + if($this->showLineNumbers) + $options['numbers']=($this->lineNumberStyle==='list')?HL_NUMBERS_LI:HL_NUMBERS_TABLE; + + $highlighter=empty($this->language)?false:Text_Highlighter::factory($this->language); + if($highlighter===false) + $o='
    '.CHtml::encode($content).'
    '; + else + { + $highlighter->setRenderer(new Text_Highlighter_Renderer_Html($options)); + $o=preg_replace('/]*>(\s*)<\/span>/','\1',$highlighter->highlight($content)); + } + + return CHtml::tag('div',$this->containerOptions,$o); + } + + /** + * Registers the needed CSS and JavaScript. + * @since 1.0.1 + */ + public function registerClientScript() + { + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + if($url===null) + $url=CHtml::asset(Yii::getPathOfAlias('system.vendors.TextHighlighter.highlight').'.css'); + Yii::app()->getClientScript()->registerCssFile($url); + } +} diff --git a/web/framework-1.0.8/web/widgets/CTreeView.php b/web/framework-1.0.8/web/widgets/CTreeView.php new file mode 100644 index 0000000..d104043 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CTreeView.php @@ -0,0 +1,232 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CTreeView displays a tree view of hierarchical data. + * + * It encapsulates the excellent tree view plugin for jQuery + * ({@link http://bassistance.de/jquery-plugins/jquery-plugin-treeview/}). + * + * To use CTreeView, simply sets {@link data} to the data that you want + * to present and you are there. + * + * CTreeView also supports dynamic data loading via AJAX. To do so, set + * {@link url} to be the URL that can serve the tree view data upon request. + * + * @author Qiang Xue + * @version $Id: CTreeView.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CTreeView extends CWidget +{ + /** + * @var array the data that can be used to generate the tree view content. + * Each array element corresponds to a tree view node with the following structure: + *
      + *
    • text: string, required, the HTML text associated with this node.
    • + *
    • expanded: boolean, optional, whether the tree view node is in expanded.
    • + *
    • id: string, optional, the ID identifying the node. This is used + * in dynamic loading of tree view (see {@link url}).
    • + *
    • hasChildren: boolean, optional, defaults to false, whether clicking on this + * node should trigger dynamic loading of more tree view nodes from server. + * The {@link url} property must be set in order to make this effective.
    • + *
    • children: array, optional, child nodes of this node.
    • + *
    + * Note, anything enclosed between the beginWidget and endWidget calls will + * also be treated as tree view content, which appends to the content generated + * from this data. + */ + public $data; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var string|array the URL to which the treeview can be dynamically loaded (in AJAX). + * See {@link CHtml::normalizeUrl} for possible URL formats. + * Setting this property will enable the dynamic treeview loading. + * When the page is displayed, the browser will request this URL with a GET parameter + * named 'source' whose value is 'root'. The server script should then generate the + * needed tree view data corresponding to the root of the tree (see {@link saveDataAsJson}.) + * When a node has a CSS class 'hasChildren', then expanding this node will also + * cause a dynamic loading of its child nodes. In this case, the value of the 'source' GET parameter + * is the 'id' property of the node. + */ + public $url; + /** + * @var string|integer animation speed. This can be one of the three predefined speeds + * ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000). + * If not set, no animation is used. + */ + public $animated; + /** + * @var boolean whether the tree should start with all branches collapsed. Defaults to false. + */ + public $collapsed; + /** + * @var string container for a tree-control, allowing the user to expand, collapse and toggle all branches with one click. + * In the container, clicking on the first hyperlink will collapse the tree; + * the second hyperlink will expand the tree; while the third hyperlink will toggle the tree. + * The property should be a valid jQuery selector (e.g. '#treecontrol' where 'treecontrol' is + * the ID of the 'div' element containing the hyperlinks.) + */ + public $control; + /** + * @var boolean set to allow only one branch on one level to be open (closing siblings which opening). + * Defaults to false. + */ + public $unique; + /** + * @var string Callback when toggling a branch. Arguments: "this" refers to the UL that was shown or hidden + */ + public $toggle; + /** + * @var string Persist the tree state in cookies or the page location. If set to "location", looks for + * the anchor that matches location.href and activates that part of the treeview it. + * Great for href-based state-saving. If set to "cookie", saves the state of the tree on + * each click to a cookie and restores that state on page load. + */ + public $persist; + /** + * @var string The cookie name to use when persisting via persist:"cookie". Defaults to 'treeview'. + */ + public $cookieId; + /** + * @var boolean Set to skip rendering of classes and hitarea divs, assuming that is done by the serverside. Defaults to false. + */ + public $prerendered; + /** + * @var array additional options that can be passed to the constructor of the treeview js object. + */ + public $options=array(); + /** + * @var array additional HTML attributes that will be rendered in the UL tag. + * The default tree view CSS has defined the following CSS classes which can be enabled + * by specifying the 'class' option here: + *
      + *
    • treeview-black
    • + *
    • treeview-gray
    • + *
    • treeview-red
    • + *
    • treeview-famfamfam
    • + *
    • filetree
    • + *
    + */ + public $htmlOptions; + + + /** + * Initializes the widget. + * This method registers all needed client scripts and renders + * the tree view content. + */ + public function init() + { + if(isset($this->htmlOptions['id'])) + $id=$this->htmlOptions['id']; + else + $id=$this->htmlOptions['id']=$this->getId(); + if($this->url!==null) + $this->url=CHtml::normalizeUrl($this->url); + $cs=Yii::app()->getClientScript(); + $cs->registerCoreScript('treeview'); + $options=$this->getClientOptions(); + $options=$options===array()?'{}' : CJavaScript::encode($options); + $cs->registerScript('Yii.CTreeView#'.$id,"jQuery(\"#{$id}\").treeview($options);"); + if($this->cssFile===null) + $cs->registerCssFile($cs->getCoreScriptUrl().'/treeview/jquery.treeview.css'); + else if($this->cssFile!==false) + $cs->registerCssFile($this->cssFile); + + echo CHtml::tag('ul',$this->htmlOptions,false,false)."\n"; + echo self::saveDataAsHtml($this->data); + } + + /** + * Ends running the widget. + */ + public function run() + { + echo ""; + } + + /** + * @return array the javascript options + */ + protected function getClientOptions() + { + $options=$this->options; + foreach(array('url','animated','collapsed','control','unique','toggle','persist','cookieId','prerendered') as $name) + { + if($this->$name!==null) + $options[$name]=$this->$name; + } + return $options; + } + + /** + * Generates tree view nodes in HTML from the data array. + * @param array the data for the tree view (see {@link data} for possible data structure). + * @return string the generated HTML for the tree view + */ + public static function saveDataAsHtml($data) + { + $html=''; + if(is_array($data)) + { + foreach($data as $node) + { + if(!isset($node['text'])) + continue; + $id=isset($node['id']) ? (' id="'.$node['id'].'"') : ''; + if(isset($node['expanded'])) + $css=$node['expanded'] ? 'open' : 'closed'; + else + $css=''; + if(isset($node['hasChildren']) && $node['hasChildren']) + { + if($css!=='') + $css.=' '; + $css.='hasChildren'; + } + if($css!=='') + $css=' class="'.$css.'"'; + $html.="{$node['text']}"; + if(isset($node['children'])) + { + $html.="\n
      \n"; + $html.=self::saveDataAsHtml($node['children']); + $html.="
    \n"; + } + $html.="\n"; + } + } + return $html; + } + + /** + * Saves tree view data in JSON format. + * This method is typically used in dynamic tree view loading + * when the server code needs to send to the client the dynamic + * tree view data. + * @param array the data for the tree view (see {@link data} for possible data structure). + * @return string the JSON representation of the data + */ + public static function saveDataAsJson($data) + { + if(empty($data)) + return '[]'; + else + return CJavaScript::jsonEncode($data); + } +} diff --git a/web/framework-1.0.8/web/widgets/CWidget.php b/web/framework-1.0.8/web/widgets/CWidget.php new file mode 100644 index 0000000..351deee --- /dev/null +++ b/web/framework-1.0.8/web/widgets/CWidget.php @@ -0,0 +1,202 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CWidget is the base class for widgets. + * + * A widget is a self-contained component that may generate presentation + * based on model data. It can be viewed as a micro-controller that embeds + * into the controller-managed views. + * + * Compared with {@link CController controller}, a widget has neither actions nor filters. + * + * @author Qiang Xue + * @version $Id: CWidget.php 916 2009-04-04 21:54:27Z qiang.xue $ + * @package system.web.widgets + * @since 1.0 + */ +class CWidget extends CBaseController +{ + /** + * @var string the prefix to the IDs of the {@link actions}. + * When a widget is declared an action provider in {@link CController::actions}, + * a prefix can be specified to differentiate its action IDs from others. + * The same prefix should then also be used to configure this property + * when the widget is used in a view of the controller. + * @since 1.0.1 + */ + public $actionPrefix; + + /** + * @var array view paths for different types of widgets + */ + private static $_viewPaths; + /** + * @var integer the counter for generating implicit IDs. + */ + private static $_counter=0; + /** + * @var string id of the widget. + */ + private $_id; + /** + * @var CBaseController owner/creator of this widget. It could be either a widget or a controller. + */ + private $_owner; + + /** + * Returns a list of actions that are used by this widget. + * The structure of this method's return value is similar to + * that returned by {@link CController::actions}. + * + * When a widget uses several actions, you can declare these actions using + * this method. The widget will then become an action provider, and the actions + * can be easily imported into a controller. + * + * Note, when creating URLs referring to the actions listed in this method, + * make sure the action IDs are prefixed with {@link actionPrefix}. + * + * @see actionPrefix + * @see CController::actions + * @since 1.0.1 + */ + public static function actions() + { + return array(); + } + + /** + * Constructor. + * @param CBaseController owner/creator of this widget. It could be either a widget or a controller. + */ + public function __construct($owner=null) + { + $this->_owner=$owner===null?Yii::app()->getController():$owner; + } + + /** + * @return CBaseController owner/creator of this widget. It could be either a widget or a controller. + */ + public function getOwner() + { + return $this->_owner; + } + + /** + * @param boolean whether to generate an ID if it is not set previously + * @return string id of the widget. + */ + public function getId($autoGenerate=true) + { + if($this->_id!==null) + return $this->_id; + else if($autoGenerate) + return $this->_id='yw'.self::$_counter++; + } + + /** + * @param string id of the widget. + */ + public function setId($value) + { + $this->_id=$value; + } + + /** + * @return CController the controller that this widget belongs to. + */ + public function getController() + { + if($this->_owner instanceof CController) + return $this->_owner; + else + return Yii::app()->getController(); + } + + /** + * Initializes the widget. + * This method is called by {@link CBaseController::createWidget} + * and {@link CBaseController::beginWidget} after the widget's + * properties have been initialized. + */ + public function init() + { + } + + /** + * Executes the widget. + * This method is called by {@link CBaseController::endWidget}. + */ + public function run() + { + } + + /** + * Returns the directory containing the view files for this widget. + * The default implementation returns the 'views' subdirectory of the directory containing the widget class file. + * @return string the directory containing the view files for this widget. + */ + public function getViewPath() + { + $className=get_class($this); + if(isset(self::$_viewPaths[$className])) + return self::$_viewPaths[$className]; + else + { + $class=new ReflectionClass(get_class($this)); + return self::$_viewPaths[$className]=dirname($class->getFileName()).DIRECTORY_SEPARATOR.'views'; + } + } + + /** + * Looks for the view script file according to the view name. + * This method will look for the view under the widget's {@link getViewPath viewPath}. + * The view script file is named as "ViewName.php". A localized view file + * may be returned if internationalization is needed. See {@link CApplication::findLocalizedFile} + * for more details. + * Since version 1.0.2, the view name can also refer to a path alias + * if it contains dot characters. + * @param string name of the view (without file extension) + * @return string the view file path. False if the view file does not exist + * @see CApplication::findLocalizedFile + */ + public function getViewFile($viewName) + { + if(strpos($viewName,'.')) // a path alias + $viewFile=Yii::getPathOfAlias($viewName).'.php'; + else + $viewFile=$this->getViewPath().DIRECTORY_SEPARATOR.$viewName.'.php'; + return is_file($viewFile) ? Yii::app()->findLocalizedFile($viewFile) : false; + } + + /** + * Renders a view. + * + * The named view refers to a PHP script (resolved via {@link getViewFile}) + * that is included by this method. If $data is an associative array, + * it will be extracted as PHP variables and made available to the script. + * + * @param string name of the view to be rendered. See {@link getViewFile} for details + * about how the view script is resolved. + * @param array data to be extracted into PHP variables and made available to the view script + * @param boolean whether the rendering result should be returned instead of being displayed to end users + * @return string the rendering result. Null if the rendering result is not required. + * @throws CException if the view does not exist + * @see getViewFile + */ + public function render($view,$data=null,$return=false) + { + if(($viewFile=$this->getViewFile($view))!==false) + return $this->renderFile($viewFile,$data,$return); + else + throw new CException(Yii::t('yii','{widget} cannot find the view "{view}".', + array('{widget}'=>get_class($this), '{view}'=>$view))); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/captcha/CCaptcha.php b/web/framework-1.0.8/web/widgets/captcha/CCaptcha.php new file mode 100644 index 0000000..2f6ce56 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/captcha/CCaptcha.php @@ -0,0 +1,132 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCaptcha renders a CAPTCHA image element. + * + * CCaptcha is used together with {@link CCaptchaAction} to provide {@link http://en.wikipedia.org/wiki/Captcha CAPTCHA} + * - a way of preventing site spam. + * + * The image element rendered by CCaptcha will display a CAPTCHA image generated + * by an action of class {@link CCaptchaAction} belonging to the current controller. + * By default, the action ID should be 'captcha', which can be changed by setting {@link captchaAction}. + * + * CCaptcha may also render a button next to the CAPTCHA image. Clicking on the button + * will change the CAPTCHA image to be a new one in an AJAX way. + * + * Since version 1.0.8, if {@link clickableImage} is set true, clicking on the CAPTCHA image + * will refresh the CAPTCHA. + * + * A {@link CCaptchaValidator} may be used to validate that the user enters + * a verification code matching the code displayed in the CAPTCHA image. + * + * @author Qiang Xue + * @version $Id: CCaptcha.php 1285 2009-08-01 01:57:50Z qiang.xue $ + * @package system.web.widgets.captcha + * @since 1.0 + */ +class CCaptcha extends CWidget +{ + /** + * @var string the ID of the action that should provide CAPTCHA image. Defaults to 'captcha', + * meaning the 'captcha' action of the current controller. This property may also + * be in the format of 'ControllerID/ActionID'. Underneath, this property is used + * by {@link CController::createUrl} to create the URL that would serve the CAPTCHA image. + * The action has to be of {@link CCaptchaAction}. + */ + public $captchaAction='captcha'; + /** + * @var boolean whether to display a button next to the CAPTCHA image. Clicking on the button + * will cause the CAPTCHA image to be changed to a new one. Defaults to true. + */ + public $showRefreshButton=true; + /** + * @var boolean whether to allow clicking on the CAPTCHA image to refresh the CAPTCHA letters. + * Defaults to false. Hint: you may want to set {@link showRefreshButton} to false if you set + * this property to be true because they serve for the same purpose. + * To enhance accessibility, you may set {@link imageOptions} to provide hints to end-users that + * the image is clickable. + * @since 1.0.8 + */ + public $clickableImage=false; + /** + * @var string the label for the refresh button. Defaults to 'Get a new code'. + */ + public $buttonLabel; + /** + * @var string the type of the refresh button. This should be either 'link' or 'button'. + * The former refers to hyperlink button while the latter a normal push button. + * Defaults to 'link'. + */ + public $buttonType='link'; + /** + * @var array HTML attributes to be applied to the rendered image element. + */ + public $imageOptions=array(); + /** + * @var array HTML attributes to be applied to the rendered refresh button element. + */ + public $buttonOptions=array(); + + + /** + * Renders the widget. + */ + public function run() + { + $this->renderImage(); + $this->registerClientScript(); + } + + /** + * Renders the CAPTCHA image. + */ + protected function renderImage() + { + if(isset($this->imageOptions['id'])) + $id=$this->imageOptions['id']; + else + $id=$this->imageOptions['id']=$this->getId(); + $url=$this->getController()->createUrl($this->captchaAction); + $alt=isset($imageOptions['alt'])?$imageOptions['alt']:''; + echo CHtml::image($url,$alt,$this->imageOptions); + } + + /** + * Registers the needed client scripts. + * @since 1.0.2 + */ + public function registerClientScript() + { + $cs=Yii::app()->clientScript; + $id=$this->imageOptions['id']; + $url=$this->getController()->createUrl($this->captchaAction,array(CCaptchaAction::REFRESH_GET_VAR=>true)); + + if($this->showRefreshButton) + { + $cs->registerScript('Yii.CCaptcha#'.$id,'dummy'); + $label=$this->buttonLabel===null?Yii::t('yii','
    Get a new code'):$this->buttonLabel; + $button=$this->buttonType==='button'?'ajaxButton':'ajaxLink'; + $html=CHtml::$button($label,$url,array('success'=>'js:function(html){jQuery("#'.$id.'").attr("src",html)}'),$this->buttonOptions); + $js="jQuery('img#$id').after(\"".CJavaScript::quote($html).'");'; + $cs->registerScript('Yii.CCaptcha#'.$id,$js); + } + + if($this->clickableImage) + { + $js="jQuery('#$id').click(function(){" + .CHtml::ajax(array( + 'url'=>$url, + 'success'=>"js:function(html){jQuery('#$id').attr('src',html)}", + )).'});'; + $cs->registerScript('Yii.CCaptcha#2'.$id,$js); + } + } +} diff --git a/web/framework-1.0.8/web/widgets/captcha/CCaptchaAction.php b/web/framework-1.0.8/web/widgets/captcha/CCaptchaAction.php new file mode 100644 index 0000000..fa009a2 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/captcha/CCaptchaAction.php @@ -0,0 +1,233 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCaptchaAction renders a CAPTCHA image. + * + * CCaptchaAction is used together with {@link CCaptcha} and {@link CCaptchaValidator} + * to provide the {@link http://en.wikipedia.org/wiki/Captcha CAPTCHA} feature. + * + * You must configure properties of CCaptchaAction to customize the appearance of + * the generated image. + * + * Note, CCaptchaAction requires PHP GD2 extension. + * + * Using CAPTCHA involves the following steps: + *
      + *
    1. Override {@link CController::actions()} and register an action of class CCaptchaAction with ID 'captcha'.
    2. + *
    3. In the form model, declare an attribute to store user-entered verification code, and declare the attribute + * to be validated by the 'captcha' validator.
    4. + *
    5. In the controller view, insert a {@link CCaptcha} widget in the form.
    6. + *
    + * + * @author Qiang Xue + * @version $Id: CCaptchaAction.php 1066 2009-05-26 15:23:48Z qiang.xue $ + * @package system.web.widgets.captcha + * @since 1.0 + */ +class CCaptchaAction extends CAction +{ + /** + * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated. + */ + const REFRESH_GET_VAR='refresh'; + /** + * Prefix to the session variable name used by the action. + */ + const SESSION_VAR_PREFIX='Yii.CCaptchaAction.'; + /** + * @var integer how many times should the same CAPTCHA be displayed. Defaults to 3. + */ + public $testLimit=3; + /** + * @var integer the width of the generated CAPTCHA image. Defaults to 120. + */ + public $width=120; + /** + * @var integer the height of the generated CAPTCHA image. Defaults to 50. + */ + public $height=50; + /** + * @var integer padding around the text. Defaults to 2. + */ + public $padding=2; + /** + * @var integer the background color. For example, 0x55FF00. + * Defaults to 0xFFFFFF, meaning white color. + */ + public $backColor=0xFFFFFF; + /** + * @var integer the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color). + */ + public $foreColor=0x2040A0; + /** + * @var integer the minimum length for randomly generated word. Defaults to 6. + */ + public $minLength=6; + /** + * @var integer the maximum length for randomly generated word. Defaults to 7. + */ + public $maxLength=7; + /** + * @var string the TrueType font file. Defaults to Duality.ttf which is provided + * with the Yii release. + */ + public $fontFile; + + /** + * Runs the action. + * If the GET parameter {@link wsdlVar} exists, the action will serve WSDL content; + * If not, the action will handle the remote method invocation. + */ + public function run() + { + if(isset($_GET[self::REFRESH_GET_VAR])) // AJAX request for regenerating code + { + $code=$this->getVerifyCode(true); + // we add a random 'v' parameter so that FireFox can refresh the image + // when src attribute of image tag is changed + echo $this->getController()->createUrl($this->getId(),array('v'=>rand(0,10000))); + } + else + { + $session=Yii::app()->session; + $session->open(); + $name=$this->getSessionKey().'count'; + if($session[$name]===null || $session[$name]>=$this->testLimit) + $regenerate=true; + else + { + $session[$name]=$session[$name]+1; + $regenerate=false; + } + + $this->renderImage($this->getVerifyCode($regenerate)); + Yii::app()->end(); + } + } + + /** + * Gets the verification code. + * @param string whether the verification code should be regenerated. + * @return string the verification code. + */ + public function getVerifyCode($regenerate=false) + { + $session=Yii::app()->session; + $session->open(); + $name=$this->getSessionKey(); + if($session[$name]===null || $regenerate) + { + $session[$name]=$this->generateVerifyCode(); + $session[$name.'count']=1; + } + return $session[$name]; + } + + /** + * Validates the input to see if it matches the generated code. + * @param string user input + * @param boolean whether the comparison should be case-sensitive + * @return whether the input is valid + */ + public function validate($input,$caseSensitive) + { + $code=$this->getVerifyCode(); + return $caseSensitive?($input===$code):!strcasecmp($input,$code); + } + + /** + * Generates a new verification code. + * @return string the generated verification code + */ + protected function generateVerifyCode() + { + if($this->minLength<3) + $this->minLength=3; + if($this->maxLength>20) + $this->maxLength=20; + if($this->minLength>$this->maxLength) + $this->maxLength=$this->minLength; + $length=rand($this->minLength,$this->maxLength); + + $letters='bcdfghjklmnpqrstvwxyz'; + $vowels='aeiou'; + $code=''; + for($i=0;$i<$length;++$i) + { + if($i%2 && rand(0,10)>2 || !($i%2) && rand(0,10)>9) + $code.=$vowels[rand(0,4)]; + else + $code.=$letters[rand(0,20)]; + } + + return $code; + } + + /** + * Returns the session variable name used to store verification code. + * @return string the session variable name + */ + protected function getSessionKey() + { + return self::SESSION_VAR_PREFIX.Yii::app()->getId().'.'.$this->getController()->getUniqueId().'.'.$this->getId(); + } + + /** + * Renders the CAPTCHA image based on the code. + * @param string the verification code + * @return string image content + */ + protected function renderImage($code) + { + $image=imagecreatetruecolor($this->width,$this->height); + $backColor=imagecolorallocate($image, + (int)($this->backColor%0x1000000/0x10000), + (int)($this->backColor%0x10000/0x100), + $this->backColor%0x100); + imagefilledrectangle($image,0,0,$this->width,$this->height,$backColor); + imagecolordeallocate($image,$backColor); + + $foreColor=imagecolorallocate($image, + (int)($this->foreColor%0x1000000/0x10000), + (int)($this->foreColor%0x10000/0x100), + $this->foreColor%0x100); + + if($this->fontFile===null) + $this->fontFile=dirname(__FILE__).'/Duality.ttf'; + + $offset=2; + $length=strlen($code); + $box=imagettfbbox(30,0,$this->fontFile,$code); + $w=$box[4]-$box[0]-$offset*($length-1); + $h=$box[1]-$box[5]; + $scale=min(($this->width-$this->padding*2)/$w,($this->height-$this->padding*2)/$h); + $x=10; + $y=round($this->height*27/40); + for($i=0;$i<$length;++$i) + { + $fontSize=(int)(rand(26,32)*$scale*0.8); + $angle=rand(-10,10); + $letter=$code[$i]; + $box=imagettftext($image,$fontSize,$angle,$x,$y,$foreColor,$this->fontFile,$letter); + $x=$box[2]-$offset; + } + + imagecolordeallocate($image,$foreColor); + + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Content-Transfer-Encoding: binary'); + header("Content-type: image/png"); + imagepng($image); + imagedestroy($image); + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/captcha/Duality.ttf b/web/framework-1.0.8/web/widgets/captcha/Duality.ttf new file mode 100644 index 0000000..581d5ce Binary files /dev/null and b/web/framework-1.0.8/web/widgets/captcha/Duality.ttf differ diff --git a/web/framework-1.0.8/web/widgets/pagers/CBasePager.php b/web/framework-1.0.8/web/widgets/pagers/CBasePager.php new file mode 100644 index 0000000..1fd913d --- /dev/null +++ b/web/framework-1.0.8/web/widgets/pagers/CBasePager.php @@ -0,0 +1,129 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CBasePager is the base class for all pagers. + * + * It provides the calculation of page count and maintains the current page. + * + * @author Qiang Xue + * @version $Id: CBasePager.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets.pagers + * @since 1.0 + */ +abstract class CBasePager extends CWidget +{ + private $_pages; + + /** + * Returns the pagination information used by this pager. + * @return CPagination the pagination information + */ + public function getPages() + { + if($this->_pages===null) + $this->_pages=$this->createPages(); + return $this->_pages; + } + + /** + * Sets the pagination information used by this pager. + * @param CPagination the pagination information + */ + public function setPages($pages) + { + $this->_pages=$pages; + } + + /** + * Creates the default pagination. + * This is called by {@link getPages} when the pagination is not set before. + * @return CPagination the default pagination instance. + */ + protected function createPages() + { + return new CPagination; + } + + /** + * @return integer number of items in each page. + * @see CPagination::getPageSize + */ + public function getPageSize() + { + return $this->getPages()->getPageSize(); + } + + /** + * @param integer number of items in each page + * @see CPagination::setPageSize + */ + public function setPageSize($value) + { + $this->getPages()->setPageSize($value); + } + + /** + * @return integer total number of items. + * @see CPagination::getItemCount + */ + public function getItemCount() + { + return $this->getPages()->getItemCount(); + } + + /** + * @param integer total number of items. + * @see CPagination::setItemCount + */ + public function setItemCount($value) + { + $this->getPages()->setItemCount($value); + } + + /** + * @return integer number of pages + * @see CPagination::getPageCount + */ + public function getPageCount() + { + return $this->getPages()->getPageCount(); + } + + /** + * @param boolean whether to recalculate the current page based on the page size and item count. + * @return integer the zero-based index of the current page. Defaults to 0. + * @see CPagination::getCurrentPage + */ + public function getCurrentPage($recalculate=true) + { + return $this->getPages()->getCurrentPage($recalculate); + } + + /** + * @param integer the zero-based index of the current page. + * @see CPagination::setCurrentPage + */ + public function setCurrentPage($value) + { + $this->getPages()->setCurrentPage($value); + } + + /** + * Creates the URL suitable for pagination. + * @param integer the page that the URL should point to. + * @return string the created URL + * @see CPagination::createPageUrl + */ + protected function createPageUrl($page) + { + return $this->getPages()->createPageUrl($this->getController(),$page); + } +} diff --git a/web/framework-1.0.8/web/widgets/pagers/CLinkPager.php b/web/framework-1.0.8/web/widgets/pagers/CLinkPager.php new file mode 100644 index 0000000..2f6909d --- /dev/null +++ b/web/framework-1.0.8/web/widgets/pagers/CLinkPager.php @@ -0,0 +1,194 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CLinkPager displays a list of hyperlinks that lead to different pages of target. + * + * @author Qiang Xue + * @version $Id: CLinkPager.php 1035 2009-05-20 13:11:46Z qiang.xue $ + * @package system.web.widgets.pagers + * @since 1.0 + */ +class CLinkPager extends CBasePager +{ + const CSS_FIRST_PAGE='first'; + const CSS_LAST_PAGE='last'; + const CSS_PREVIOUS_PAGE='previous'; + const CSS_NEXT_PAGE='next'; + const CSS_INTERNAL_PAGE='page'; + const CSS_HIDDEN_PAGE='hidden'; + const CSS_SELECTED_PAGE='selected'; + + /** + * @var integer maximum number of page buttons that can be displayed. Defaults to 10. + */ + public $maxButtonCount=12; + /** + * @var string the text label for the next page button. Defaults to 'Next >'. + */ + public $nextPageLabel; + /** + * @var string the text label for the previous page button. Defaults to '< Previous'. + */ + public $prevPageLabel; + /** + * @var string the text label for the first page button. Defaults to '<< First'. + */ + public $firstPageLabel; + /** + * @var string the text label for the last page button. Defaults to 'Last >>'. + */ + public $lastPageLabel; + /** + * @var string the text shown before page buttons. Defaults to 'Go to page: '. + */ + public $header; + /** + * @var string the text shown after page buttons. + */ + public $footer=''; + /** + * @var mixed the CSS file used for the widget. Defaults to null, meaning + * using the default CSS file included together with the widget. + * If false, no CSS file will be used. Otherwise, the specified CSS file + * will be included when using this widget. + */ + public $cssFile; + /** + * @var array HTML attributes for the pager container tag. + */ + public $htmlOptions=array(); + + + /** + * Executes the widget. + * This overrides the parent implementation by displaying the generated page buttons. + */ + public function run() + { + if($this->nextPageLabel===null) + $this->nextPageLabel=Yii::t('yii','Next >'); + if($this->prevPageLabel===null) + $this->prevPageLabel=Yii::t('yii','< Previous'); + if($this->firstPageLabel===null) + $this->firstPageLabel=Yii::t('yii','<< First'); + if($this->lastPageLabel===null) + $this->lastPageLabel=Yii::t('yii','Last >>'); + if($this->header===null) + $this->header=Yii::t('yii','Go to page: '); + + $buttons=$this->createPageButtons(); + + if(empty($buttons)) + return; + + $this->registerClientScript(); + + $htmlOptions=$this->htmlOptions; + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=$this->getId(); + if(!isset($htmlOptions['class'])) + $htmlOptions['class']='yiiPager'; + echo $this->header; + echo CHtml::tag('ul',$htmlOptions,implode("\n",$buttons)); + echo $this->footer; + } + + /** + * Creates the page buttons. + * @return array a list of page buttons (in HTML code). + */ + protected function createPageButtons() + { + if(($pageCount=$this->getPageCount())<=1) + return array(); + + list($beginPage,$endPage)=$this->getPageRange(); + $currentPage=$this->getCurrentPage(false); // currentPage is calculated in getPageRange() + $buttons=array(); + + // first page + $buttons[]=$this->createPageButton($this->firstPageLabel,0,self::CSS_FIRST_PAGE,$beginPage<=0,false); + + // prev page + if(($page=$currentPage-1)<0) + $page=0; + $buttons[]=$this->createPageButton($this->prevPageLabel,$page,self::CSS_PREVIOUS_PAGE,$currentPage<=0,false); + + // internal pages + for($i=$beginPage;$i<=$endPage;++$i) + $buttons[]=$this->createPageButton($i+1,$i,self::CSS_INTERNAL_PAGE,false,$i==$currentPage); + + // next page + if(($page=$currentPage+1)>=$pageCount-1) + $page=$pageCount-1; + $buttons[]=$this->createPageButton($this->nextPageLabel,$page,self::CSS_NEXT_PAGE,$currentPage>=$pageCount-1,false); + + // last page + $buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,self::CSS_LAST_PAGE,$endPage>=$pageCount-1,false); + + return $buttons; + } + + /** + * Creates a page button. + * You may override this method to customize the page buttons. + * @param string the text label for the button + * @param integer the page number + * @param string the CSS class for the page button. This could be 'page', 'first', 'last', 'next' or 'previous'. + * @param boolean whether this page button is visible + * @param boolean whether this page button is selected + * @return string the generated button + */ + protected function createPageButton($label,$page,$class,$hidden,$selected) + { + if($hidden || $selected) + $class.=' '.($hidden ? self::CSS_HIDDEN_PAGE : self::CSS_SELECTED_PAGE); + return '
  • '.CHtml::link($label,$this->createPageUrl($page)).'
  • '; + } + + /** + * @return array the begin and end pages that need to be displayed. + */ + protected function getPageRange() + { + $currentPage=$this->getCurrentPage(); + $pageCount=$this->getPageCount(); + + $beginPage=max(0, $currentPage-(int)($this->maxButtonCount/2)); + if(($endPage=$beginPage+$this->maxButtonCount-1)>=$pageCount) + { + $endPage=$pageCount-1; + $beginPage=max(0,$endPage-$this->maxButtonCount+1); + } + return array($beginPage,$endPage); + } + + /** + * Registers the needed client scripts (mainly CSS file). + */ + public function registerClientScript() + { + if($this->cssFile!==false) + self::registerCssFile($this->cssFile); + } + + /** + * Registers the needed CSS file. + * @param string the CSS URL. If null, a default CSS URL will be used. + * @since 1.0.2 + */ + public static function registerCssFile($url=null) + { + if($url===null) + $url=CHtml::asset(Yii::getPathOfAlias('system.web.widgets.pagers.pager').'.css'); + Yii::app()->getClientScript()->registerCssFile($url); + } +} diff --git a/web/framework-1.0.8/web/widgets/pagers/CListPager.php b/web/framework-1.0.8/web/widgets/pagers/CListPager.php new file mode 100644 index 0000000..777040c --- /dev/null +++ b/web/framework-1.0.8/web/widgets/pagers/CListPager.php @@ -0,0 +1,85 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + + +/** + * CListPager displays a dropdown list that contains options leading to different pages of target. + * + * @author Qiang Xue + * @version $Id: CListPager.php 537 2009-01-15 17:00:34Z qiang.xue $ + * @package system.web.widgets.pagers + * @since 1.0 + */ +class CListPager extends CBasePager +{ + /** + * @var string the text shown before page buttons. Defaults to 'Go to page: '. + */ + public $header; + /** + * @var string the text shown after page buttons. + */ + public $footer; + /** + * @var string the text displayed as a prompt option in the dropdown list. Defaults to null, meaning no prompt. + */ + public $promptText; + /** + * @var string the format string used to generate page selection text. + * The sprintf function will be used to perform the formatting. + */ + public $pageTextFormat; + /** + * @var array HTML attributes for the enclosing 'div' tag. + */ + public $htmlOptions=array(); + + + /** + * Executes the widget. + * This overrides the parent implementation by displaying the generated page buttons. + */ + public function run() + { + if(($pageCount=$this->getPageCount())<=1) + return; + $pages=array(); + for($i=0;$i<$pageCount;++$i) + $pages[$this->createPageUrl($i)]=$this->generatePageText($i); + $selection=$this->createPageUrl($this->getCurrentPage()); + $options=array('onchange'=>'if(this.value!=\'\') {window.location=this.value;};'); + if($this->promptText!==null) + $options['prompt']=$this->promptText; + $content=CHtml::dropDownList($this->getId(),$selection,$pages,$options); + $htmlOptions=$this->htmlOptions; + if(!isset($htmlOptions['id'])) + $htmlOptions['id']=$this->getId(); + + if($this->header===null) + $this->header=Yii::t('yii','Go to page: '); + + echo CHtml::tag('div',$htmlOptions,$this->header.$content.$this->footer); + } + + /** + * Generates the list option for the specified page number. + * You may override this method to customize the option display. + * @param integer zero-based page number + * @return string the list option for the page number + */ + protected function generatePageText($page) + { + if($this->pageTextFormat!==null) + return sprintf($this->pageTextFormat,$page+1); + else + return $page+1; + } +} \ No newline at end of file diff --git a/web/framework-1.0.8/web/widgets/pagers/pager.css b/web/framework-1.0.8/web/widgets/pagers/pager.css new file mode 100644 index 0000000..23cfdc1 --- /dev/null +++ b/web/framework-1.0.8/web/widgets/pagers/pager.css @@ -0,0 +1,68 @@ +/** + * CSS styles for CLinkPager. + * + * @author Qiang Xue + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: pager.css 433 2008-12-30 22:59:17Z qiang.xue $ + * @since 1.0 + */ + +ul.yiiPager +{ + font-size:11px; + border:0; + margin:0; + padding:0; + line-height:100%; + display:inline; +} + +ul.yiiPager li +{ + display:inline; +} + +ul.yiiPager a:link, +ul.yiiPager a:visited +{ + border:solid 1px #9aafe5; + font-weight:bold; + color:#0e509e; + padding:1px 6px; + text-decoration:none; +} + +ul.yiiPager .page a +{ + font-weight:normal; +} + +ul.yiiPager a:hover +{ + border:solid 1px #0e509e; +} + +ul.yiiPager .selected a +{ + background:#2e6ab1; + color:#FFFFFF; + font-weight:bold; +} + +ul.yiiPager .hidden a +{ + border:solid 1px #DEDEDE; + color:#888888; +} + +/** + * Hide first and last buttons by default. + */ +/*ul.yiiPager .first, +ul.yiiPager .last +{ + display:none; +}*/ + diff --git a/web/framework-1.0.8/web/widgets/views/flexWidget.php b/web/framework-1.0.8/web/widgets/views/flexWidget.php new file mode 100644 index 0000000..13ad44f --- /dev/null +++ b/web/framework-1.0.8/web/widgets/views/flexWidget.php @@ -0,0 +1,98 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: flexWidget.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system.web.widgets.views + * @since 1.0 + */ +?> + + \ No newline at end of file diff --git a/web/framework-1.0.8/yii.php b/web/framework-1.0.8/yii.php new file mode 100644 index 0000000..b382266 --- /dev/null +++ b/web/framework-1.0.8/yii.php @@ -0,0 +1,29 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + * @version $Id: yii.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system + * @since 1.0 + */ + +require(dirname(__FILE__).'/YiiBase.php'); + +/** + * Yii is a helper class serving common framework functionalities. + * + * It encapsulates {@link YiiBase} which provides the actual implementation. + * By writing your own Yii class, you can customize some functionalities of YiiBase. + * + * @author Qiang Xue + * @version $Id: yii.php 433 2008-12-30 22:59:17Z qiang.xue $ + * @package system + * @since 1.0 + */ +class Yii extends YiiBase +{ +} diff --git a/web/images/41.png b/web/images/41.png new file mode 100644 index 0000000..f66d3c2 Binary files /dev/null and b/web/images/41.png differ diff --git a/web/images/amc.jpg b/web/images/amc.jpg new file mode 100644 index 0000000..eb12ef4 Binary files /dev/null and b/web/images/amc.jpg differ diff --git a/web/images/base/action_refresh_blue.gif b/web/images/base/action_refresh_blue.gif new file mode 100644 index 0000000..7c8bcd0 Binary files /dev/null and b/web/images/base/action_refresh_blue.gif differ diff --git a/web/images/base/action_save.gif b/web/images/base/action_save.gif new file mode 100644 index 0000000..6e6f7de Binary files /dev/null and b/web/images/base/action_save.gif differ diff --git a/web/images/base/delete.png b/web/images/base/delete.png new file mode 100644 index 0000000..840f7db Binary files /dev/null and b/web/images/base/delete.png differ diff --git a/web/images/base/dot.png b/web/images/base/dot.png new file mode 100644 index 0000000..66f9ab1 Binary files /dev/null and b/web/images/base/dot.png differ diff --git a/web/images/base/download.png b/web/images/base/download.png new file mode 100644 index 0000000..a762db3 Binary files /dev/null and b/web/images/base/download.png differ diff --git a/web/images/base/edit.png b/web/images/base/edit.png new file mode 100644 index 0000000..4126975 Binary files /dev/null and b/web/images/base/edit.png differ diff --git a/web/images/base/enroll.png b/web/images/base/enroll.png new file mode 100644 index 0000000..f1e45cf Binary files /dev/null and b/web/images/base/enroll.png differ diff --git a/web/images/base/favorite.png b/web/images/base/favorite.png new file mode 100644 index 0000000..9af39ea Binary files /dev/null and b/web/images/base/favorite.png differ diff --git a/web/images/base/folder_new.png b/web/images/base/folder_new.png new file mode 100644 index 0000000..8109ed8 Binary files /dev/null and b/web/images/base/folder_new.png differ diff --git a/web/images/base/new.png b/web/images/base/new.png new file mode 100644 index 0000000..74d36bd Binary files /dev/null and b/web/images/base/new.png differ diff --git a/web/images/base/newcomment.png b/web/images/base/newcomment.png new file mode 100644 index 0000000..c1f3965 Binary files /dev/null and b/web/images/base/newcomment.png differ diff --git a/web/images/base/newfile.png b/web/images/base/newfile.png new file mode 100644 index 0000000..6e2700a Binary files /dev/null and b/web/images/base/newfile.png differ diff --git a/web/images/base/newfolder.png b/web/images/base/newfolder.png new file mode 100644 index 0000000..8109ed8 Binary files /dev/null and b/web/images/base/newfolder.png differ diff --git a/web/images/base/newuser.png b/web/images/base/newuser.png new file mode 100644 index 0000000..deae99b Binary files /dev/null and b/web/images/base/newuser.png differ diff --git a/web/images/base/pause.png b/web/images/base/pause.png new file mode 100644 index 0000000..6051be9 Binary files /dev/null and b/web/images/base/pause.png differ diff --git a/web/images/base/play.png b/web/images/base/play.png new file mode 100644 index 0000000..3f4a0ab Binary files /dev/null and b/web/images/base/play.png differ diff --git a/web/images/base/tools.png b/web/images/base/tools.png new file mode 100644 index 0000000..f9c503e Binary files /dev/null and b/web/images/base/tools.png differ diff --git a/web/images/base/top.png b/web/images/base/top.png new file mode 100644 index 0000000..3de9a44 Binary files /dev/null and b/web/images/base/top.png differ diff --git a/web/images/base/top_header.png b/web/images/base/top_header.png new file mode 100644 index 0000000..cc13abf Binary files /dev/null and b/web/images/base/top_header.png differ diff --git a/web/images/base/top_header_selected.png b/web/images/base/top_header_selected.png new file mode 100644 index 0000000..5febe2e Binary files /dev/null and b/web/images/base/top_header_selected.png differ diff --git a/web/images/base/video_play.png b/web/images/base/video_play.png new file mode 100644 index 0000000..7733dad Binary files /dev/null and b/web/images/base/video_play.png differ diff --git a/web/images/beta_corner_banner.png b/web/images/beta_corner_banner.png new file mode 100644 index 0000000..46427c8 Binary files /dev/null and b/web/images/beta_corner_banner.png differ diff --git a/web/images/beta_corner_banner2.png b/web/images/beta_corner_banner2.png new file mode 100644 index 0000000..a91bd4f Binary files /dev/null and b/web/images/beta_corner_banner2.png differ diff --git a/web/images/btc.png b/web/images/btc.png new file mode 100644 index 0000000..3852708 Binary files /dev/null and b/web/images/btc.png differ diff --git a/web/images/coin-154.png b/web/images/coin-154.png new file mode 100644 index 0000000..d4dd065 Binary files /dev/null and b/web/images/coin-154.png differ diff --git a/web/images/coin-22.png b/web/images/coin-22.png new file mode 100644 index 0000000..a43fc82 Binary files /dev/null and b/web/images/coin-22.png differ diff --git a/web/images/coin-251.png b/web/images/coin-251.png new file mode 100644 index 0000000..cf9a321 Binary files /dev/null and b/web/images/coin-251.png differ diff --git a/web/images/coin-30.png b/web/images/coin-30.png new file mode 100644 index 0000000..2845d33 Binary files /dev/null and b/web/images/coin-30.png differ diff --git a/web/images/coin-316.png b/web/images/coin-316.png new file mode 100644 index 0000000..d2866ec Binary files /dev/null and b/web/images/coin-316.png differ diff --git a/web/images/coin-33.png b/web/images/coin-33.png new file mode 100644 index 0000000..9757835 Binary files /dev/null and b/web/images/coin-33.png differ diff --git a/web/images/coin-6.png b/web/images/coin-6.png new file mode 100644 index 0000000..3852708 Binary files /dev/null and b/web/images/coin-6.png differ diff --git a/web/images/coin-834.png b/web/images/coin-834.png new file mode 100644 index 0000000..d2866ec Binary files /dev/null and b/web/images/coin-834.png differ diff --git a/web/images/gears.png b/web/images/gears.png new file mode 100644 index 0000000..fb00863 Binary files /dev/null and b/web/images/gears.png differ diff --git a/web/images/minus2-78.png b/web/images/minus2-78.png new file mode 100644 index 0000000..c3c8040 Binary files /dev/null and b/web/images/minus2-78.png differ diff --git a/web/images/next.png b/web/images/next.png new file mode 100644 index 0000000..231eedb Binary files /dev/null and b/web/images/next.png differ diff --git a/web/images/pause.png b/web/images/pause.png new file mode 100644 index 0000000..5a853a9 Binary files /dev/null and b/web/images/pause.png differ diff --git a/web/images/play.png b/web/images/play.png new file mode 100644 index 0000000..a50661a Binary files /dev/null and b/web/images/play.png differ diff --git a/web/images/plus2-78.png b/web/images/plus2-78.png new file mode 100644 index 0000000..de3adec Binary files /dev/null and b/web/images/plus2-78.png differ diff --git a/web/images/sign-in-with-twitter-gray.png b/web/images/sign-in-with-twitter-gray.png new file mode 100644 index 0000000..c2822dc Binary files /dev/null and b/web/images/sign-in-with-twitter-gray.png differ diff --git a/web/images/sign-in-with-twitter-link.png b/web/images/sign-in-with-twitter-link.png new file mode 100644 index 0000000..7dd94c7 Binary files /dev/null and b/web/images/sign-in-with-twitter-link.png differ diff --git a/web/images/start.png b/web/images/start.png new file mode 100644 index 0000000..5165880 Binary files /dev/null and b/web/images/start.png differ diff --git a/web/images/t_mini-a.png b/web/images/t_mini-a.png new file mode 100644 index 0000000..b6fb357 Binary files /dev/null and b/web/images/t_mini-a.png differ diff --git a/web/images/tweet.jpg b/web/images/tweet.jpg new file mode 100644 index 0000000..c69179e Binary files /dev/null and b/web/images/tweet.jpg differ diff --git a/web/images/twitter.jpg b/web/images/twitter.jpg new file mode 100644 index 0000000..e061415 Binary files /dev/null and b/web/images/twitter.jpg differ diff --git a/web/images/ui/16x16_2leftarrow.png b/web/images/ui/16x16_2leftarrow.png new file mode 100644 index 0000000..4c8b73a Binary files /dev/null and b/web/images/ui/16x16_2leftarrow.png differ diff --git a/web/images/ui/16x16_2rightarrow.png b/web/images/ui/16x16_2rightarrow.png new file mode 100644 index 0000000..805f764 Binary files /dev/null and b/web/images/ui/16x16_2rightarrow.png differ diff --git a/web/images/ui/16x16_bottom.png b/web/images/ui/16x16_bottom.png new file mode 100644 index 0000000..a762db3 Binary files /dev/null and b/web/images/ui/16x16_bottom.png differ diff --git a/web/images/ui/16x16_chat.png b/web/images/ui/16x16_chat.png new file mode 100644 index 0000000..72e7b8e Binary files /dev/null and b/web/images/ui/16x16_chat.png differ diff --git a/web/images/ui/16x16_delete.png b/web/images/ui/16x16_delete.png new file mode 100644 index 0000000..a432b49 Binary files /dev/null and b/web/images/ui/16x16_delete.png differ diff --git a/web/images/ui/16x16_edit.png b/web/images/ui/16x16_edit.png new file mode 100644 index 0000000..33de951 Binary files /dev/null and b/web/images/ui/16x16_edit.png differ diff --git a/web/images/ui/16x16_email.png b/web/images/ui/16x16_email.png new file mode 100644 index 0000000..694a8e0 Binary files /dev/null and b/web/images/ui/16x16_email.png differ diff --git a/web/images/ui/16x16_fileclose.png b/web/images/ui/16x16_fileclose.png new file mode 100644 index 0000000..3bf5029 Binary files /dev/null and b/web/images/ui/16x16_fileclose.png differ diff --git a/web/images/ui/16x16_kcontrol.png b/web/images/ui/16x16_kcontrol.png new file mode 100644 index 0000000..f9c503e Binary files /dev/null and b/web/images/ui/16x16_kcontrol.png differ diff --git a/web/images/ui/16x16_kuser.png b/web/images/ui/16x16_kuser.png new file mode 100644 index 0000000..8e649a8 Binary files /dev/null and b/web/images/ui/16x16_kuser.png differ diff --git a/web/images/ui/16x16_ledgreen.png b/web/images/ui/16x16_ledgreen.png new file mode 100644 index 0000000..08b9c9d Binary files /dev/null and b/web/images/ui/16x16_ledgreen.png differ diff --git a/web/images/ui/16x16_link.png b/web/images/ui/16x16_link.png new file mode 100644 index 0000000..f17d26b Binary files /dev/null and b/web/images/ui/16x16_link.png differ diff --git a/web/images/ui/16x16_misc.png b/web/images/ui/16x16_misc.png new file mode 100644 index 0000000..98d4b99 Binary files /dev/null and b/web/images/ui/16x16_misc.png differ diff --git a/web/images/ui/16x16_package_edutainment.png b/web/images/ui/16x16_package_edutainment.png new file mode 100644 index 0000000..be9ea79 Binary files /dev/null and b/web/images/ui/16x16_package_edutainment.png differ diff --git a/web/images/ui/16x16_window_list.png b/web/images/ui/16x16_window_list.png new file mode 100644 index 0000000..085baff Binary files /dev/null and b/web/images/ui/16x16_window_list.png differ diff --git a/web/images/ui/22x22_bottom.png b/web/images/ui/22x22_bottom.png new file mode 100644 index 0000000..ef2dcac Binary files /dev/null and b/web/images/ui/22x22_bottom.png differ diff --git a/web/images/ui/24x24_link.png b/web/images/ui/24x24_link.png new file mode 100644 index 0000000..1bf2402 Binary files /dev/null and b/web/images/ui/24x24_link.png differ diff --git a/web/images/ui/32x32_link.png b/web/images/ui/32x32_link.png new file mode 100644 index 0000000..af1c4d0 Binary files /dev/null and b/web/images/ui/32x32_link.png differ diff --git a/web/images/ui/48x48_link.png b/web/images/ui/48x48_link.png new file mode 100644 index 0000000..58121f5 Binary files /dev/null and b/web/images/ui/48x48_link.png differ diff --git a/web/images/ui/arrow-down.gif b/web/images/ui/arrow-down.gif new file mode 100644 index 0000000..939f9ff Binary files /dev/null and b/web/images/ui/arrow-down.gif differ diff --git a/web/images/ui/arrow-right.gif b/web/images/ui/arrow-right.gif new file mode 100644 index 0000000..43b522c Binary files /dev/null and b/web/images/ui/arrow-right.gif differ diff --git a/web/images/ui/asc.gif b/web/images/ui/asc.gif new file mode 100644 index 0000000..7415786 Binary files /dev/null and b/web/images/ui/asc.gif differ diff --git a/web/images/ui/away.gif b/web/images/ui/away.gif new file mode 100644 index 0000000..4ddc3df Binary files /dev/null and b/web/images/ui/away.gif differ diff --git a/web/images/ui/bg.gif b/web/images/ui/bg.gif new file mode 100644 index 0000000..fac668f Binary files /dev/null and b/web/images/ui/bg.gif differ diff --git a/web/images/ui/control_play_blue.png b/web/images/ui/control_play_blue.png new file mode 100644 index 0000000..f8c8ec6 Binary files /dev/null and b/web/images/ui/control_play_blue.png differ diff --git a/web/images/ui/desc.gif b/web/images/ui/desc.gif new file mode 100644 index 0000000..3b30b3c Binary files /dev/null and b/web/images/ui/desc.gif differ diff --git a/web/images/ui/down_arrow.png b/web/images/ui/down_arrow.png new file mode 100644 index 0000000..8231898 Binary files /dev/null and b/web/images/ui/down_arrow.png differ diff --git a/web/images/ui/empty.png b/web/images/ui/empty.png new file mode 100644 index 0000000..1afa081 Binary files /dev/null and b/web/images/ui/empty.png differ diff --git a/web/images/ui/expand_down.png b/web/images/ui/expand_down.png new file mode 100644 index 0000000..59022ab Binary files /dev/null and b/web/images/ui/expand_down.png differ diff --git a/web/images/ui/expand_up.png b/web/images/ui/expand_up.png new file mode 100644 index 0000000..f338c58 Binary files /dev/null and b/web/images/ui/expand_up.png differ diff --git a/web/images/ui/green-check.png b/web/images/ui/green-check.png new file mode 100644 index 0000000..af66f64 Binary files /dev/null and b/web/images/ui/green-check.png differ diff --git a/web/images/ui/icon-check.gif b/web/images/ui/icon-check.gif new file mode 100644 index 0000000..84de08b Binary files /dev/null and b/web/images/ui/icon-check.gif differ diff --git a/web/images/ui/install-flash.jpg b/web/images/ui/install-flash.jpg new file mode 100644 index 0000000..ea808d8 Binary files /dev/null and b/web/images/ui/install-flash.jpg differ diff --git a/web/images/ui/linkout.png b/web/images/ui/linkout.png new file mode 100644 index 0000000..7cba135 Binary files /dev/null and b/web/images/ui/linkout.png differ diff --git a/web/images/ui/loading.gif b/web/images/ui/loading.gif new file mode 100644 index 0000000..cf370f3 Binary files /dev/null and b/web/images/ui/loading.gif differ diff --git a/web/images/ui/loading_white.gif b/web/images/ui/loading_white.gif new file mode 100644 index 0000000..e0e6e97 Binary files /dev/null and b/web/images/ui/loading_white.gif differ diff --git a/web/images/ui/menudot.png b/web/images/ui/menudot.png new file mode 100644 index 0000000..66f9ab1 Binary files /dev/null and b/web/images/ui/menudot.png differ diff --git a/web/images/ui/menuo_bg.gif b/web/images/ui/menuo_bg.gif new file mode 100644 index 0000000..2b8dd70 Binary files /dev/null and b/web/images/ui/menuo_bg.gif differ diff --git a/web/images/ui/offline.gif b/web/images/ui/offline.gif new file mode 100644 index 0000000..bb46413 Binary files /dev/null and b/web/images/ui/offline.gif differ diff --git a/web/images/ui/online.gif b/web/images/ui/online.gif new file mode 100644 index 0000000..212e6e9 Binary files /dev/null and b/web/images/ui/online.gif differ diff --git a/web/images/ui/shade.gif b/web/images/ui/shade.gif new file mode 100644 index 0000000..2dd6249 Binary files /dev/null and b/web/images/ui/shade.gif differ diff --git a/web/images/ui/shadeactive.gif b/web/images/ui/shadeactive.gif new file mode 100644 index 0000000..0a78232 Binary files /dev/null and b/web/images/ui/shadeactive.gif differ diff --git a/web/images/ui/showactivity.png b/web/images/ui/showactivity.png new file mode 100644 index 0000000..6c86544 Binary files /dev/null and b/web/images/ui/showactivity.png differ diff --git a/web/images/ui/showcourse.png b/web/images/ui/showcourse.png new file mode 100644 index 0000000..8e649a8 Binary files /dev/null and b/web/images/ui/showcourse.png differ diff --git a/web/images/ui/showdetail.png b/web/images/ui/showdetail.png new file mode 100644 index 0000000..08894ba Binary files /dev/null and b/web/images/ui/showdetail.png differ diff --git a/web/images/ui/showfile.png b/web/images/ui/showfile.png new file mode 100644 index 0000000..dab56c9 Binary files /dev/null and b/web/images/ui/showfile.png differ diff --git a/web/images/ui/showfolder.png b/web/images/ui/showfolder.png new file mode 100644 index 0000000..e5ed97f Binary files /dev/null and b/web/images/ui/showfolder.png differ diff --git a/web/images/ui/showgraph.png b/web/images/ui/showgraph.png new file mode 100644 index 0000000..7f8b8cc Binary files /dev/null and b/web/images/ui/showgraph.png differ diff --git a/web/images/ui/showlink.png b/web/images/ui/showlink.png new file mode 100644 index 0000000..fee4cd4 Binary files /dev/null and b/web/images/ui/showlink.png differ diff --git a/web/images/ui/showmedium.png b/web/images/ui/showmedium.png new file mode 100644 index 0000000..547f9ef Binary files /dev/null and b/web/images/ui/showmedium.png differ diff --git a/web/images/ui/showsmall.png b/web/images/ui/showsmall.png new file mode 100644 index 0000000..776bb6b Binary files /dev/null and b/web/images/ui/showsmall.png differ diff --git a/web/images/ui/tintblue.gif b/web/images/ui/tintblue.gif new file mode 100644 index 0000000..f830740 Binary files /dev/null and b/web/images/ui/tintblue.gif differ diff --git a/web/images/ui/tintbluedark.gif b/web/images/ui/tintbluedark.gif new file mode 100644 index 0000000..0600cc6 Binary files /dev/null and b/web/images/ui/tintbluedark.gif differ diff --git a/web/images/vn_embed.png b/web/images/vn_embed.png new file mode 100644 index 0000000..e1ea8a9 Binary files /dev/null and b/web/images/vn_embed.png differ diff --git a/web/index.php b/web/index.php new file mode 100644 index 0000000..c6691ce --- /dev/null +++ b/web/index.php @@ -0,0 +1,30 @@ +run(); +} + +catch(CException $e) +{ +// Javascript("window.history.go(-1)"); +// mydump($e, 3); + + debuglog("front end error ".$_SERVER['REMOTE_ADDR']); + debuglog($e->getMessage()); + +// send_email_alert('frontend', "frontend error", "a frontend error occured"); +} + + diff --git a/web/loop2.sh b/web/loop2.sh new file mode 100755 index 0000000..eed2c00 --- /dev/null +++ b/web/loop2.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd /var/web +while true; do + php5 run.php cronjob/runloop2 + sleep 60 +done +exec bash + diff --git a/web/main.sh b/web/main.sh new file mode 100755 index 0000000..c033e73 --- /dev/null +++ b/web/main.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd /var/web +while true; do + php5 run.php cronjob/run + sleep 90 +done +exec bash + diff --git a/web/robots.txt b/web/robots.txt new file mode 100644 index 0000000..e4b001e --- /dev/null +++ b/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * + diff --git a/web/serverconfig.php b/web/serverconfig.php new file mode 100644 index 0000000..d77b268 --- /dev/null +++ b/web/serverconfig.php @@ -0,0 +1,26 @@ + 0.10, +); + + diff --git a/web/yaamp/components/CUFHtml.php b/web/yaamp/components/CUFHtml.php new file mode 100644 index 0000000..fb40787 --- /dev/null +++ b/web/yaamp/components/CUFHtml.php @@ -0,0 +1,136 @@ +getErrors() as $attribute=>$errors) + { + foreach($errors as $error) + { + if($error!='') + $content.="
  • $error
  • \n"; + } + } + } + if($content!=='') + { + $header='

    '.Yii::t('yii','Please fix the following input errors:').'

    '; + return self::tag('div',array('id'=>self::$errorSummaryCss),$header."\n
      \n$content
    ".$footer); + } + else + return ''; + } + + + public static function activeLabel($model,$attribute,$htmlOptions=array()) + { + $for=self::getIdByName(self::resolveName($model,$attribute)); + if(isset($htmlOptions['label'])) + { + $label=$htmlOptions['label']; + unset($htmlOptions['label']); + } + else + $label=$model->getAttributeLabel($attribute); + return self::label($label,$for,$htmlOptions); + } + + public static function activeLabelEx($model,$attribute,$htmlOptions=array()) + { + $realAttribute=$attribute; + self::resolveName($model,$attribute); // strip off square brackets if any + $htmlOptions['required']=$model->isAttributeRequired($attribute,self::$scenario); + return self::activeLabel($model,$realAttribute,$htmlOptions); + } + + public static function submitButton($label='submit',$htmlOptions=array()) + { + $htmlOptions['type']='submit'; + $htmlOptions = self::addUniFormClass($htmlOptions, 'submitButton'); + return self::button($label,$htmlOptions); + } + + public static function resetButton($label='reset',$htmlOptions=array()) + { + $htmlOptions['type']='reset'; + $htmlOptions = self::addUniFormClass($htmlOptions, 'resetButton'); + // Workaround for IE alignment + return self::tag('div', array('class'=>'resetButton', self::button($label, $htmlOptions))); + } + + public static function activeTextField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + $htmlOptions = self::addUniFormClass($htmlOptions, 'textInput'); + $htmlOptions = self::addUniFormClass($htmlOptions, 'tweetnews-input'); + return self::activeInputField('text',$model,$attribute,$htmlOptions); + } + + protected static function activeInputField($type,$model,$attribute,$htmlOptions) + { + $htmlOptions['type']=$type; + if($type==='file') + unset($htmlOptions['value']); + else if(!isset($htmlOptions['value'])) + $htmlOptions['value']=$model->$attribute; + return self::tag('input',$htmlOptions); + } + + public static function openActiveCtrlHolder($model,$attribute,$htmlOptions=array()) + { + $opentag=''; + if($model->hasErrors($attribute)) + { + self::addErrorCss($htmlOptions); + $opentag = '

    '.$model->getError($attribute)."

    \n"; + } + $opentag = self::openCtrlHolder($htmlOptions)."\n".$opentag; + return $opentag; + } + + public static function openCtrlHolder($htmlOptions=array()) + { + if (isset($htmlOptions['class'])) + $htmlOptions['class'] = $htmlOptions['class'].' ctrlHolder'; + else + $htmlOptions['class'] = 'ctrlHolder'; + return self::openTag('div', $htmlOptions); + } + + public static function closeCtrlHolder() + { + return "
    ".self::closeTag('div')."\n"; + } + + protected static function addUniFormClass($htmlOptions, $class) + { + if (isset($htmlOptions['class'])) + $htmlOptions['class'] = $htmlOptions['class'].' '.$class; + else + $htmlOptions['class'] = $class; + return $htmlOptions; + } +} + diff --git a/web/yaamp/components/UniForm.php b/web/yaamp/components/UniForm.php new file mode 100644 index 0000000..026dcf0 --- /dev/null +++ b/web/yaamp/components/UniForm.php @@ -0,0 +1,25 @@ +* '; + CHtml::$errorSummaryCss = 'errorMsg'; + } +} + diff --git a/web/yaamp/components/UserIdentity.php b/web/yaamp/components/UserIdentity.php new file mode 100644 index 0000000..0db7a14 --- /dev/null +++ b/web/yaamp/components/UserIdentity.php @@ -0,0 +1,25 @@ +_id = $user->id; + $this->_fullname = $user->name; + $this->username = $user->logon; + } + + public function getId() + { + return $this->_id; + } + + public function getFullname() + { + return $this->_fullname; + } +} + diff --git a/web/yaamp/config.php b/web/yaamp/config.php new file mode 100644 index 0000000..2b051e5 --- /dev/null +++ b/web/yaamp/config.php @@ -0,0 +1,68 @@ +'yaamp.com', + + 'defaultController'=>'site', + 'layout'=>'main', + + 'basePath'=>YAAMP_HTDOCS."/yaamp", + 'controllerPath'=>'yaamp/modules', + 'viewPath'=>'yaamp/modules', + 'layoutPath'=>'yaamp/ui', + + 'preload'=>array('log'), + 'import'=>array('application.components.*'), +// 'params'=>require("siteconfig.php"), + + 'components'=>array( + + 'urlManager'=>array( + 'urlFormat'=>'path', + 'showScriptName'=>false, + 'appendParams'=>false, + ), + + 'assetManager'=>array( + 'basePath'=>YAAMP_HTDOCS."/assets"), + + 'log'=>array( + 'class'=>'CLogRouter', + 'routes'=>array( + array( + 'class'=>'CFileLogRoute', + 'levels'=>'error, warning', + // 'levels'=>'trace, error, warning', + ), +// array( +// 'class'=>'CProfileLogRoute', +// 'report'=>'summary', +// ), + ), + ), + + 'user'=>array( + 'allowAutoLogin'=>true, + 'loginUrl'=>array('site/login'), + ), + + 'db'=>array( + 'class'=>'CDbConnection', + 'connectionString'=>"mysql:host=".YAAMP_DBHOST.";dbname=".YAAMP_DBNAME, + + 'username'=>YAAMP_DBUSER, + 'password'=>YAAMP_DBPASSWORD, + + 'enableProfiling'=>false, + 'charset'=>'utf8', + 'schemaCachingDuration'=>3600, + ), + ), + + +); + + + + + diff --git a/web/yaamp/core/backend/backend.php b/web/yaamp/core/backend/backend.php new file mode 100644 index 0000000..d747b37 --- /dev/null +++ b/web/yaamp/core/backend/backend.php @@ -0,0 +1,17 @@ +name $db_block->height"); + $reward = $db_block->amount; + + $total_hash_power = dboscalar("select sum(difficulty) from shares where valid and algo='$coin->algo'"); + if(!$total_hash_power) return; + + $list = dbolist("SELECT userid, sum(difficulty) as total from shares where valid and algo='$coin->algo' group by userid"); + foreach($list as $item) + { + $hash_power = $item['total']; + if(!$hash_power) continue; + + $user = getdbo('db_accounts', $item['userid']); + if(!$user) continue; + + $amount = $reward * $hash_power / $total_hash_power; + if(!$user->no_fees) $amount = take_yaamp_fee($amount, $coin->algo); + + $earning = new db_earnings; + $earning->userid = $user->id; + $earning->coinid = $coin->id; + $earning->blockid = $db_block->id; + $earning->create_time = $db_block->time; + $earning->amount = $amount; + $earning->price = $coin->price; + + if($db_block->category == 'generate') + { + $earning->mature_time = time(); + $earning->status = 1; + } + else // immature + $earning->status = 0; + + $earning->save(); + + $user->last_login = time(); + $user->save(); + } + + $delay = time() - 5*60; + dborun("delete from shares where algo='$coin->algo' and time<$delay"); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +function BackendBlockFind1() +{ +// debuglog(__METHOD__); + $list = getdbolist('db_blocks', "category='new' order by time"); + foreach($list as $db_block) + { + $coin = getdbo('db_coins', $db_block->coin_id); + if(!$coin->enable) continue; + + $db_block->category = 'orphan'; + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $block = $remote->getblock($db_block->blockhash); + if(!$block || !isset($block['tx']) || !isset($block['tx'][0])) + { + $db_block->save(); + continue; + } + + $tx = $remote->gettransaction($block['tx'][0]); + if(!$tx || !isset($tx['details']) || !isset($tx['details'][0])) + { + $db_block->save(); + continue; + } + + $db_block->txhash = $block['tx'][0]; + $db_block->category = 'immature'; //$tx['details'][0]['category']; + $db_block->amount = $tx['details'][0]['amount']; + $db_block->confirmations = $tx['confirmations']; + $db_block->price = $coin->price; + $db_block->save(); + + if($db_block->category != 'orphan') + BackendBlockNew($coin, $db_block); + } +} + +///////////////////////////////////////////////////////////////////////////////// + +function BackendBlocksUpdate() +{ +// debuglog(__METHOD__); + $t1 = microtime(true); + + $list = getdbolist('db_blocks', "category='immature' order by time"); + foreach($list as $block) + { + $coin = getdbo('db_coins', $block->coin_id); + if(!$coin || !$coin->enable) + { + $block->delete(); + continue; + } + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + if(empty($block->txhash)) + { + $blockext = $remote->getblock($block->blockhash); + if(!$blockext || !isset($blockext['tx'][0])) continue; + + $block->txhash = $blockext['tx'][0]; + } + + $tx = $remote->gettransaction($block->txhash); + if(!$tx) continue; + + $block->confirmations = $tx['confirmations']; + + if($block->confirmations == -1) + $block->category = 'orphan'; + + else if(isset($tx['details']) && isset($tx['details'][0])) + $block->category = $tx['details'][0]['category']; + + else if(isset($tx['category'])) + $block->category = $tx['category']; + + $block->save(); + + if($block->category == 'generate') + dborun("update earnings set status=1, mature_time=UNIX_TIMESTAMP() where blockid=$block->id"); + + else if($block->category != 'immature') + dborun("delete from earnings where blockid=$block->id"); + } + + $d1 = microtime(true) - $t1; + controller()->memcache->add_monitoring_function(__METHOD__, $d1); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +function BackendBlockFind2() +{ + $coins = getdbolist('db_coins', "enable"); + foreach($coins as $coin) + { + if($coin->symbol == 'BTC') continue; + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $mostrecent = 0; + if($coin->lastblock == null) $coin->lastblock = ''; + $list = $remote->listsinceblock($coin->lastblock); + if(!$list) continue; + +// debuglog("find2 $coin->symbol"); + foreach($list['transactions'] as $transaction) + { + if($transaction['time'] > time() - 5*60) continue; + if(!isset($transaction['blockhash'])) continue; + + if($transaction['time'] > $mostrecent) + { + $coin->lastblock = $transaction['blockhash']; + $mostrecent = $transaction['time']; + } + + if($transaction['time'] < time() - 60*60) continue; + if($transaction['category'] != 'generate' && $transaction['category'] != 'immature') continue; + + $blockext = $remote->getblock($transaction['blockhash']); + + $db_block = getdbosql('db_blocks', "blockhash='{$transaction['blockhash']}' or height={$blockext['height']}"); + if($db_block) continue; + +// debuglog("adding lost block $coin->name {$blockext['height']}"); + + $db_block = new db_blocks; + $db_block->blockhash = $transaction['blockhash']; + $db_block->coin_id = $coin->id; + $db_block->category = 'immature'; //$transaction['category']; + $db_block->time = $transaction['time']; + $db_block->amount = $transaction['amount']; + $db_block->confirmations = $transaction['confirmations']; + $db_block->height = $blockext['height']; + $db_block->difficulty = $blockext['difficulty']; + $db_block->price = $coin->price; + $db_block->algo = $coin->algo; + $db_block->save(); + + BackendBlockNew($coin, $db_block); + } + + $coin->save(); + } +} + +function MonitorBTC() +{ +// debuglog(__FUNCTION__); + + $coin = getdbosql('db_coins', "symbol='BTC'"); + if(!$coin) return; + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + if(!$remote) return; + + $mostrecent = 0; + if($coin->lastblock == null) $coin->lastblock = ''; + $list = $remote->listsinceblock($coin->lastblock); + if(!$list) return; + + $coin->lastblock = $list['lastblock']; + $coin->save(); + + foreach($list['transactions'] as $transaction) + { + if(!isset($transaction['blockhash'])) continue; + if($transaction['confirmations'] == 0) continue; + if($transaction['category'] != 'send') continue; + if($transaction['fee'] != -0.0001) continue; + + debuglog(__FUNCTION__); + debuglog($transaction); + + $txurl = "https://blockchain.info/tx/{$transaction['txid']}"; + + $b = mail('yaamp201@gmail.com', "withdraw {$transaction['amount']}", + "{$transaction['address']}"); + + if(!$b) debuglog('error sending email'); + + } + + +} + + + + + + diff --git a/web/yaamp/core/backend/clear.php b/web/yaamp/core/backend/clear.php new file mode 100644 index 0000000..f34342d --- /dev/null +++ b/web/yaamp/core/backend/clear.php @@ -0,0 +1,47 @@ +userid); + if(!$user) + { + $earning->delete(); + continue; + } + + $coin = getdbo('db_coins', $earning->coinid); + if(!$coin) + { + $earning->delete(); + continue; + } + + $earning->status = 2; // cleared + $earning->price = $coin->price; + $earning->save(); + +// $refcoin = getdbo('db_coins', $user->coinid); +// if($refcoin && $refcoin->price<=0) continue; +// $value = $earning->amount * $coin->price / ($refcoin? $refcoin->price: 1); + + $value = yaamp_convert_amount_user($coin, $earning->amount, $user); + + $user->balance += $value; + $user->save(); + + if($user->coinid == 6) + $total_cleared += $value; + } + + if($total_cleared>0) + debuglog("total cleared from mining $total_cleared BTC"); +} + diff --git a/web/yaamp/core/backend/coins.php b/web/yaamp/core/backend/coins.php new file mode 100644 index 0000000..6296536 --- /dev/null +++ b/web/yaamp/core/backend/coins.php @@ -0,0 +1,245 @@ +name"); + + $coin = getdbo('db_coins', $coin->id); + if(!$coin) continue; + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $info = $remote->getinfo(); + if(!$info) + { + $coin->enable = false; + // $coin->auto_ready = false; + $coin->connections = 0; + + $coin->save(); + continue; + } + +// debuglog($info); + $coin->enable = true; + + if(isset($info['difficulty'])) + $difficulty = $info['difficulty']; + else + $difficulty = $remote->getdifficulty(); + + if(is_array($difficulty)) + { + $coin->difficulty = $difficulty['proof-of-work']; + if(isset($difficulty['proof-of-stake'])) + $coin->difficulty_pos = $difficulty['proof-of-stake']; + } + else + $coin->difficulty = $difficulty; + + if($coin->algo == 'quark') + $coin->difficulty /= 0x100; + + if($coin->difficulty == 0) + $coin->difficulty = 1; + + $coin->errors = isset($info['errors'])? $info['errors']: ''; + $coin->txfee = isset($info['paytxfee'])? $info['paytxfee']: ''; + $coin->connections = isset($info['connections'])? $info['connections']: ''; + $coin->balance = isset($info['balance'])? $info['balance']: 0; + $coin->mint = dboscalar("select sum(amount) from blocks where coin_id=$coin->id and category='immature'"); + + if(empty($coin->master_wallet)) + { + $coin->master_wallet = $remote->getaccountaddress(''); + // debuglog($coin->master_wallet); + } + + if(empty($coin->rpcencoding)) + { + $difficulty = $remote->getdifficulty(); + if(is_array($difficulty)) + $coin->rpcencoding = 'POS'; + else + $coin->rpcencoding = 'POW'; + } + + if($coin->hassubmitblock == NULL) + { + $remote->submitblock(''); + if(strcasecmp($remote->error, 'method not found') == 0) + $coin->hassubmitblock = false; + else + $coin->hassubmitblock = true; + } + + if($coin->auxpow == NULL) + { + $ret = $remote->getauxblock(); + + if(strcasecmp($remote->error, 'method not found') == 0) + $coin->auxpow = false; + else + $coin->auxpow = true; + } + +// if($coin->symbol != 'BTC') +// { +// if($coin->symbol == 'PPC') +// $template = $remote->getblocktemplate(''); +// else + $template = $remote->getblocktemplate('{}'); + + if($template && isset($template['coinbasevalue'])) + { + $coin->reward = $template['coinbasevalue']/100000000*$coin->reward_mul; + + if($coin->symbol == 'TAC' && isset($template['_V2'])) + $coin->charity_amount = $template['_V2']/100000000; + + if(isset($template['payee_amount']) && $coin->symbol != 'LIMX') + { + $coin->charity_amount = $template['payee_amount']/100000000; + $coin->reward -= $coin->charity_amount; + + // debuglog("$coin->symbol $coin->charity_amount $coin->reward"); + } + + else if(!empty($coin->charity_address)) + { + if($coin->charity_amount) + ; //$coin->reward -= $coin->charity_amount; + else + $coin->reward -= $coin->reward * $coin->charity_percent / 100; + } + + if(isset($template['bits'])) + { + $target = decode_compact($template['bits']); + $coin->difficulty = target_to_diff($target); + } + } + + else if(strcasecmp($remote->error, 'method not found') == 0) + { + $template = $remote->getmemorypool(); + if($template && isset($template['coinbasevalue'])) + { + $coin->usememorypool = true; + $coin->reward = $template['coinbasevalue']/100000000*$coin->reward_mul; + + if(isset($template['bits'])) + { + $target = decode_compact($template['bits']); + $coin->difficulty = target_to_diff($target); + } + } + + else + { + $coin->auto_ready = false; + $coin->errors = $remote->error; + } + } + + else + { + $coin->auto_ready = false; + $coin->errors = $remote->error; + } + + if(strcasecmp($coin->errors, 'No more PoW blocks') == 0) + { + $coin->dontsell = true; + $coin->auto_ready = false; + } +// } + + if($coin->block_height != $info['blocks']) + { + $count = $info['blocks'] - $coin->block_height; + $ttf = (time() - $coin->last_network_found) / $count; + + if(empty($coin->actual_ttf)) $coin->actual_ttf = $ttf; + + $coin->actual_ttf = percent_feedback($coin->actual_ttf, $ttf, 5); + $coin->last_network_found = time(); + } + + $coin->version = $info['version']; + $coin->block_height = $info['blocks']; + + $coin->save(); + // debuglog(" end $coin->name"); + + } + + $coins = getdbolist('db_coins', "enable order by auxpow desc"); + foreach($coins as $coin) + { + $coin = getdbo('db_coins', $coin->id); + if(!$coin) continue; + + if($coin->difficulty) + { + $coin->index_avg = $coin->reward * $coin->price * 10000 / $coin->difficulty; + if(!$coin->auxpow && $coin->rpcencoding == 'POW') + { + $indexaux = dboscalar("select sum(index_avg) from coins where enable and visible and auto_ready and auxpow and algo='$coin->algo'"); + $coin->index_avg += $indexaux; + } + } + + if($coin->network_hash) + $coin->network_ttf = $coin->difficulty * 0x100000000 / $coin->network_hash; + + if(isset($pool_rate[$coin->algo])) + $coin->pool_ttf = $coin->difficulty * 0x100000000 / $pool_rate[$coin->algo]; + + if(strstr($coin->image, 'http')) + { + $data = file_get_contents($coin->image); + $coin->image = "/images/coin-$coin->id.png"; + + @unlink(YAAMP_HTDOCS.$coin->image); + file_put_contents(YAAMP_HTDOCS.$coin->image, $data); + } + + $coin->save(); + } + + $d1 = microtime(true) - $t1; + controller()->memcache->add_monitoring_function(__METHOD__, $d1); +} + + + + diff --git a/web/yaamp/core/backend/markets.php b/web/yaamp/core/backend/markets.php new file mode 100644 index 0000000..ba95ddc --- /dev/null +++ b/web/yaamp/core/backend/markets.php @@ -0,0 +1,505 @@ +symbol2'"); + if(!$coin) continue; + + $list = getdbolist('db_markets', "coinid=$coin->id"); + foreach($list as $market) + { + $market2 = getdbosql('db_markets', "coinid=$coin2->id and name='$market->name'"); + if(!$market2) continue; + + $market2->price = $market->price; + $market2->price2 = $market->price2; + $market2->deposit_address = $market->deposit_address; + + $market2->save(); + } + } + + $coins = getdbolist('db_coins', "installed and id in (select distinct coinid from markets)"); + foreach($coins as $coin) + { + if($coin->symbol=='BTC') + { + $coin->price = 1; + $coin->price2 = 1; + + $coin->save(); + continue; + } + + $market = getBestMarket($coin); + if($market) + { + $coin->price = $market->price*(1-YAAMP_FEES_EXCHANGE/100); + $coin->price2 = $market->price2; + + $base_coin = !empty($market->base_coin)? getdbosql('db_coins', "symbol='$market->base_coin'"): null; + if($base_coin) + { + $coin->price *= $base_coin->price; + $coin->price2 *= $base_coin->price; + } + +// if($market->name == 'c-cex') +// $coin->price *= 0.95; + } + + else + { + $coin->price = 0; + $coin->price2 = 0; + } + + $coin->save(); + dborun("update earnings set price=$coin->price where status!=2 and coinid=$coin->id"); + } +} + +function getBestMarket($coin) +{ + $market = getdbosql('db_markets', "coinid=$coin->id and price!=0 and + deposit_address is not null and deposit_address != '' and + (name='bittrex' or name='cryptsy') order by price desc"); + + if(!$market) + { + $market = getdbosql('db_markets', "coinid=$coin->id and price!=0 and + deposit_address is not null and deposit_address != '' and + name!='yobit' order by price desc"); + + if(!$market) + $market = getdbosql('db_markets', "coinid=$coin->id and price!=0 and + deposit_address is not null and deposit_address != '' + order by price desc"); + } + + return $market; +} + +function AverageIncrement($value1, $value2) +{ + $percent = 80; + $value = ($value1*(100-$percent) + $value2*$percent) / 100; + + return $value; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function updateBleutradeMarkets() +{ + $list = bleutrade_api_query('public/getcurrencies'); + if(!$list) return; + + foreach($list->result as $currency) + { + // debuglog($currency); + if($currency->Currency == 'BTC') continue; + + $coin = getdbosql('db_coins', "symbol='$currency->Currency'"); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='bleutrade'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'bleutrade'; + } + + $market->txfee = $currency->TxFee; + if(!$currency->IsActive) + { + $market->price = 0; + $market->save(); + + continue; + } + + $market->save(); + $pair = "{$coin->symbol}_BTC"; + + $ticker = bleutrade_api_query('public/getticker', "&market=$pair"); + if(!$ticker || !$ticker->success || !$ticker->result) continue; + + if(empty($market->deposit_address)) + { + $address = bleutrade_api_query('account/getdepositaddress', "¤cy=$coin->symbol"); + + if($address && isset($address->result)) + $market->deposit_address = $address->result->Address; + } + + $price2 = ($ticker->result[0]->Bid+$ticker->result[0]->Ask)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->result[0]->Bid); + + $market->save(); + } + +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +function updateBittrexMarkets() +{ + $list = bittrex_api_query('public/getcurrencies'); + if(!$list) return; + + foreach($list->result as $currency) + { + if($currency->Currency == 'BTC') continue; + + $coin = getdbosql('db_coins', "symbol='$currency->Currency'"); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='bittrex'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'bittrex'; + } + + $market->txfee = $currency->TxFee; + $market->message = $currency->Notice; + + if($coin->symbol == 'EGMA') + { + $market->price = 0.00000001; + $market->save(); + + continue; + } + + if(!$currency->IsActive) + { + $market->price = 0; + $market->save(); + + continue; + } + + $market->save(); + $pair = "BTC-$coin->symbol"; + + $ticker = bittrex_api_query('public/getticker', "&market=$pair"); + if(!$ticker || !$ticker->success || !$ticker->result) continue; + + if(empty($market->deposit_address)) + { + $address = bittrex_api_query('account/getdepositaddress', "¤cy=$coin->symbol"); + + if($address && isset($address->result)) + $market->deposit_address = $address->result->Address; + } + + $price2 = ($ticker->result->Bid+$ticker->result->Ask)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->result->Bid); + + $market->save(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +function updateCryptsyMarkets() +{ + // dborun("update markets set price=0 where name='cryptsy'"); + // return; + + $markets = cryptsy_api_query('getmarkets'); + if(!$markets || !isset($markets['return'])) + { + debuglog($markets); + return; + } + + $list = cryptsy_api_query('getcoindata'); + if(!$list || !isset($list['return'])) + { + debuglog($list); + return; + } + + foreach($list['return'] as $currency) + { + if($currency['code'] == 'BTC') continue; + $symbol = $currency['code']; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='cryptsy'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'cryptsy'; + + foreach($markets['return'] as $item) + { + if($item['secondary_currency_code'] != 'BTC') continue; + if($item['primary_currency_code'] != $symbol) continue; + + $market->marketid = $item['marketid']; + } + } + + if(empty($market->marketid)) + { + foreach($markets['return'] as $item) + { + if($item['secondary_currency_code'] != 'BTC') continue; + if($item['primary_currency_code'] != $symbol) continue; + + $market->marketid = $item['marketid']; + } + } + + $market->txfee = $currency['withdrawalfee']*100; + switch($currency['maintenancemode']) + { + case 0: + $market->message = ''; + break; + case 1: + $market->message = 'Maintenance'; + break; + case 2: + $market->message = 'Updating Wallet'; + break; + case 3: + $market->message = 'Network Issues'; + break; + default: + $market->message = 'Unknown Error'; + break; + } + + $market->save(); + + if($currency['maintenancemode']) + { + $market->price = 0; + $market->save(); + + continue; + } + + $ticker = getCryptsyTicker($market->marketid); + // debuglog($ticker); + if(!$ticker) continue; + if(!isset($ticker->return->$symbol->buyorders[0])) + { + debuglog("error cryptsy $coin->name"); + debuglog($ticker, 5); + continue; + } + + $price2 = ($ticker->return->$symbol->buyorders[0]->price+$ticker->return->$symbol->sellorders[0]->price)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->return->$symbol->buyorders[0]->price); + + $market->save(); + } + + $list = cryptsy_api_query('getmydepositaddresses'); + foreach($list['return'] as $symbol=>$item) + { + // debuglog($item); + if($symbol == 'BTC') continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='cryptsy'"); + if(!$market) continue; + + $market->deposit_address = $item; + $market->save(); + } + +} + +//////////////////////////////////////////////////////////////////////////////////// + +function updateCCexMarkets() +{ + // dborun("update markets set price=0 where name='c-cex'"); <- add that line + $ccex = new CcexAPI; + + $list = $ccex->getPairs(); + foreach($list as $item) + { + $e = explode('-', $item); + if(!isset($e[1])) continue; + if($e[1] != 'btc') continue; + + $symbol = strtoupper($e[0]); + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='c-cex'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'c-cex'; + } + + $market->save(); + + $ticker = $ccex->getTickerInfo($item); + if(!$ticker) continue; + + $price2 = ($ticker['buy']+$ticker['sell'])/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker['buy']); + + $market->save(); + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +function updatePoloniexMarkets() +{ + $poloniex = new poloniex; + + $tickers = $poloniex->get_ticker(); + if(!$tickers) return; + + foreach($tickers as $symbol=>$ticker) + { + $a = explode('_', $symbol); + if(!isset($a[1])) continue; + if($a[0] != 'BTC') continue; + + $symbol = $a[1]; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='poloniex'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'poloniex'; + } + + if(empty($market->deposit_address) && $coin->installed) + $poloniex->generate_address($coin->symbol); + + $price2 = ($ticker['highestBid']+$ticker['lowestAsk'])/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker['highestBid']); + + $market->save(); + } + + $list = $poloniex->get_deposit_addresses(); + foreach($list as $symbol=>$item) + { + if($symbol == 'BTC') continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='poloniex'"); + if(!$market) continue; + + $market->deposit_address = $item; + $market->save(); + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +function updateYobitMarkets() +{ + $res = yobit_api_query('info'); + if(!$res) return; + + foreach($res->pairs as $i=>$item) + { + $e = explode('_', $i); + $symbol = strtoupper($e[0]); + if($e[1] != 'btc') continue; + if($symbol == 'BTC') continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || !$coin->installed) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='yobit'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = 'yobit'; + } + + $pair = strtolower($coin->symbol).'_btc'; + + $ticker = yobit_api_query("ticker/$pair"); + if(!$ticker) continue; + + $price2 = ($ticker->$pair->buy+$ticker->$pair->sell)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->$pair->buy); + + $market->save(); + } +} + +function updateJubiMarkets() +{ + $btc = jubi_api_query('ticker', "?coin=btc"); + if(!$btc) continue; + + $list = getdbolist('db_markets', "name='jubi'"); + foreach($list as $market) + { + $coin = getdbo('db_coins', $market->coinid); + if(!$coin) continue; + + $lowsymbol = strtolower($coin->symbol); + + $ticker = jubi_api_query('ticker', "?coin=$lowsymbol"); + if(!$ticker) continue; + + $ticker->buy /= $btc->sell; + $ticker->sell /= $btc->buy; + + $price2 = ($ticker->buy+$ticker->sell)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->buy*0.95); + + $market->save(); + } +} + + + + + diff --git a/web/yaamp/core/backend/payment.php b/web/yaamp/core/backend/payment.php new file mode 100644 index 0000000..fa9b5c3 --- /dev/null +++ b/web/yaamp/core/backend/payment.php @@ -0,0 +1,153 @@ +symbol"); + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $info = $remote->getinfo(); + if(!$info) + { + debuglog("$coin->symbol cant connect to coin"); + return; + } + + $min = 0.001; + +// if(date("w", time()) == 0 && date("H", time()) > 12) // sunday afternoon +// $min = 0.0001; + + $users = getdbolist('db_accounts', "balance>$min and coinid=$coin->id"); + + if($coin->symbol == 'MUE' || $coin->symbol == 'DIME') + { + foreach($users as $user) + { + $user = getdbo('db_accounts', $user->id); + if(!$user) continue; + + $amount = $user->balance; + while($user->balance > $min && $amount > $min) + { + debuglog("$coin->symbol sendtoaddress $user->username $amount"); + $tx = $remote->sendtoaddress($user->username, round($amount, 8)); + if(!$tx) + { + debuglog("error $remote->error, $user->username, $amount"); + if($remote->error == 'transaction too large' || $remote->error == 'invalid amount') + { + $amount /= 2; + continue; + } + + break; + } + + $payout = new db_payouts; + $payout->account_id = $user->id; + $payout->time = time(); + $payout->amount = bitcoinvaluetoa($amount); + $payout->fee = 0; + $payout->tx = $tx; + $payout->save(); + + $user->balance -= $amount; + $user->save(); + } + } + + debuglog("payment done"); + return; + } + + $total_to_pay = 0; + $addresses = array(); + + foreach($users as $user) + { + $total_to_pay += round($user->balance, 8); + $addresses[$user->username] = round($user->balance, 8); + } + + if(!$total_to_pay) + { + // debuglog("nothing to pay"); + return; + } + + if($info['balance']-0.001 < $total_to_pay) + { + debuglog("$coin->symbol wallet insufficient funds for payment {$info['balance']} < $total_to_pay"); + return; + } + + if($coin->symbol=='BTC') + { + global $cold_wallet_table; + + $balance = $info['balance']; + $stats = getdbosql('db_stats', "1 order by time desc"); + + $renter = dboscalar("select sum(balance) from renters"); + $pie = $balance - $total_to_pay - $renter - 1; + + debuglog("pie to split is $pie"); + if($pie>0) + { + foreach($cold_wallet_table as $coldwallet=>$percent) + { + $coldamount = round($pie * $percent, 8); + if($coldamount < $min) break; + + debuglog("paying cold wallet $coldwallet $coldamount"); + + $addresses[$coldwallet] = $coldamount; + $total_to_pay += $coldamount; + } + } + } + + debuglog("paying $total_to_pay $coin->symbol min is $min"); + + $tx = $remote->sendmany('', $addresses, 1, ''); + if(!$tx) + { + debuglog($remote->error); + return; + } + + foreach($users as $user) + { + $user = getdbo('db_accounts', $user->id); + if(!$user) continue; + + $payout = new db_payouts; + $payout->account_id = $user->id; + $payout->time = time(); + $payout->amount = bitcoinvaluetoa($user->balance); + $payout->fee = 0; + $payout->tx = $tx; + $payout->save(); + + $user->balance = 0; + $user->save(); + } + + debuglog("payment done"); + sleep(5); +} + + + + + + diff --git a/web/yaamp/core/backend/rawcoins.php b/web/yaamp/core/backend/rawcoins.php new file mode 100644 index 0000000..52d2192 --- /dev/null +++ b/web/yaamp/core/backend/rawcoins.php @@ -0,0 +1,144 @@ +result)) + { + dborun("update markets set deleted=true where name='bittrex'"); + foreach($list->result as $currency) + updateRawCoin('bittrex', $currency->Currency, $currency->CurrencyLong); + } + + $list = bleutrade_api_query('public/getcurrencies'); + if(isset($list->result)) + { + dborun("update markets set deleted=true where name='bleutrade'"); + foreach($list->result as $currency) + updateRawCoin('bleutrade', $currency->Currency, $currency->CurrencyLong); + } + + $list = cryptsy_api_query('getmarkets'); + if(isset($list['return'])) + { + dborun("update markets set deleted=true where name='cryptsy'"); + foreach($list['return'] as $item) + updateRawCoin('cryptsy', $item['primary_currency_code'], $item['primary_currency_name']); + } + + $ccex = new CcexAPI; + $list = $ccex->getPairs(); + if($list) + { + dborun("update markets set deleted=true where name='c-cex'"); + foreach($list as $item) + { + $e = explode('-', $item); + $symbol = strtoupper($e[0]); + + updateRawCoin('c-cex', $symbol); + } + } + + $poloniex = new poloniex; + $tickers = $poloniex->get_currencies(); + + dborun("update markets set deleted=true where name='poloniex'"); + foreach($tickers as $symbol=>$ticker) + { + if($ticker['disabled']) continue; + if($ticker['delisted']) continue; + + updateRawCoin('poloniex', $symbol); + } + + $res = yobit_api_query('info'); + if($res) + { + dborun("update markets set deleted=true where name='yobit'"); + foreach($res->pairs as $i=>$item) + { + $e = explode('_', $i); + $symbol = strtoupper($e[0]); + + updateRawCoin('yobit', $symbol); + } + } + + ////////////////////////////////////////////////////////// + + dborun("delete from markets where deleted"); + + $list = getdbolist('db_coins', "not enable and not installed and id not in (select distinct coinid from markets)"); + foreach($list as $coin) + { + debuglog("$coin->symbol is not longer active"); + $coin->delete(); + } +} + +function updateRawCoin($marketname, $symbol, $name='unknown') +{ + if($symbol == 'BTC') return; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) + { + debuglog("new coin $marketname $symbol $name"); + + $coin = new db_coins; + $coin->txmessage = true; + $coin->hassubmitblock = true; + $coin->name = $name; + $coin->symbol = $symbol; + $coin->created = time(); + $coin->save(); + + mail('yaamp201@gmail.com', "New coin $symbol", "new coin $symbol ($name) on $marketname"); + sleep(30); + } + + else if($coin->name == 'unknown' && $name != 'unknown') + { + $coin->name = $name; + $coin->save(); + } + + $list = getdbolist('db_coins', "symbol=:symbol or symbol2=:symbol", array(':symbol'=>$symbol)); + foreach($list as $coin) + { + $market = getdbosql('db_markets', "coinid=$coin->id and name='$marketname'"); + if(!$market) + { + $market = new db_markets; + $market->coinid = $coin->id; + $market->name = $marketname; + } + + $market->deleted = false; + $market->save(); + } + + ///////// + +// if($coin->enable || !empty($coin->algo) || !empty($coin->errors) || $coin->name == 'unknown') return; +// debuglog("http://www.cryptocoinrank.com/$coin->name"); + +// $data = file_get_contents("http://www.cryptocoinrank.com/$coin->name"); +// if($data) +// { +// $b = preg_match('/Algo: (.*)<\/span>/', $data, $m); +// if($b) +// { +// $coin->errors = trim($m[1]); +// $coin->save(); +// } +// } + +} + + + + diff --git a/web/yaamp/core/backend/renting.php b/web/yaamp/core/backend/renting.php new file mode 100644 index 0000000..2ec2c3d --- /dev/null +++ b/web/yaamp/core/backend/renting.php @@ -0,0 +1,271 @@ +$algo)); + + dborun("update jobs set active=true where ready and price>$rent and algo=:algo", array(':algo'=>$algo)); + dborun("update jobs set active=false where active and price<$rent and algo=:algo", array(':algo'=>$algo)); + } + + $list = getdbolist('db_jobsubmits', "status=0"); + foreach($list as $submit) + { + $rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$submit->algo)); + $amount = $rent * $submit->difficulty / 20116.56761169; + + if($submit->algo == 'sha256') $amount /= 1000; + + $submit->amount = $amount - $amount*YAAMP_FEES_RENTING/100; + $submit->status = 1; + $submit->save(); + + $job = getdbo('db_jobs', $submit->jobid); + if(!$job) + { + $submit->delete(); + continue; + } + + $renter = getdbo('db_renters', $job->renterid); + if(!$renter) + { + $job->delete(); + $submit->delete(); + continue; + } + + $renter->balance -= $amount; + $renter->spent += $amount; + + if($renter->balance <= 0.00001000) + { + debuglog("resetting balance to 0, $renter->balance, $renter->id, $renter->address"); + $renter->balance = 0; + dborun("update jobs set active=false, ready=false where renterid=$renter->id"); + } + + $renter->updated = time(); + $renter->save(); + } + +// debuglog(__FUNCTION__); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +function BackendRentingPayout() +{ +// debuglog(__FUNCTION__); + + $total_cleared = 0; + foreach(yaamp_get_algos() as $algo) + { + $delay = time() - 5*60; + dborun("delete from jobsubmits where status=2 and algo=:algo and time<$delay", array(':algo'=>$algo)); + + $amount = dboscalar("select sum(amount) from jobsubmits where status=1 and algo=:algo", array(':algo'=>$algo)); + if($amount < 0.00002000) continue; + + dborun("update jobsubmits set status=2 where status=1 and algo=:algo", array(':algo'=>$algo)); + $total_cleared += $amount; + + $block = new db_blocks; + $block->coin_id = 0; + $block->time = time(); + $block->amount = $amount; + $block->price = 1; + $block->algo = $algo; + $block->category = 'generate'; + $block->save(); + + $total_hash_power = dboscalar("SELECT sum(difficulty) FROM shares where valid and algo=:algo", array(':algo'=>$algo)); + if(!$total_hash_power) continue; + + $list = dbolist("SELECT userid, sum(difficulty) as total FROM shares where valid and algo=:algo GROUP BY userid", array(':algo'=>$algo)); + foreach($list as $item) + { + $hash_power = $item['total']; + if(!$hash_power) continue; + + $user = getdbo('db_accounts', $item['userid']); + if(!$user) continue; + + $earning = new db_earnings; + $earning->userid = $user->id; + $earning->coinid = 0; + $earning->blockid = $block->id; + $earning->create_time = time(); + $earning->price = 1; + $earning->status = 2; // cleared + + $earning->amount = $amount * $hash_power / $total_hash_power; + if(!$user->no_fees) $earning->amount = take_yaamp_fee($earning->amount, $algo); + + $earning->save(); + + $refcoin = getdbo('db_coins', $user->coinid); + $value = $earning->amount / (($refcoin && $refcoin->price2)? $refcoin->price2: 1); + + // $value = yaamp_convert_amount_user($coin, $earning->amount, $user); + + $user->last_login = time(); + $user->balance += $value; + $user->save(); + } + + $delay = time() - 5*60; + dborun("delete from shares where algo=:algo and time<$delay", array(':algo'=>$algo)); + } + + if($total_cleared>0) + debuglog("total cleared from rental $total_cleared BTC"); +} + +//////////////////////////////////////////////////////////////////////////////// + +function BackendUpdateDeposit() +{ +// debuglog(__FUNCTION__); + + $btc = getdbosql('db_coins', "symbol='BTC'"); + if(!$btc) return; + + $remote = new Bitcoin($btc->rpcuser, $btc->rpcpasswd, $btc->rpchost, $btc->rpcport); + + $info = $remote->getinfo(); + if(!$info) return; + if(!isset($info['blocks'])) return; + + $hash = $remote->getblockhash(intval($info['blocks'])); + if(!$hash) return; + + $block = $remote->getblock($hash); + if(!$block) return; + + if(!isset($block['time'])) return; + if($block['time'] + 30*60 < time()) return; + + $list = $remote->listaccounts(1); + foreach($list as $r=>$a) + { + if($a == 0) continue; + + $b = preg_match('/renter-prod-([0-9]+)/', $r, $m); + if(!$b) continue; + + $renter = getdbo('db_renters', $m[1]); + if(!$renter) continue; + + $ts = $remote->listtransactions(yaamp_renter_account($renter), 1); + if(!$ts || !isset($ts[0])) continue; + + $moved = $remote->move(yaamp_renter_account($renter), '', $a); + if(!$moved) continue; + + debuglog("deposit $renter->id $renter->address, $a"); + + $rentertx = new db_rentertxs; + $rentertx->renterid = $renter->id; + $rentertx->time = time(); + $rentertx->amount = $a; + $rentertx->type = 'deposit'; + $rentertx->tx = isset($ts[0]['txid'])? $ts[0]['txid']: ''; + $rentertx->save(); + + $renter->unconfirmed = 0; + $renter->balance += $a; + $renter->updated = time(); + $renter->save(); + } + + $list = $remote->listaccounts(0); + foreach($list as $r=>$a) + { + if($a == 0) continue; + + $b = preg_match('/renter-prod-([0-9]+)/', $r, $m); + if(!$b) continue; + + $renter = getdbo('db_renters', $m[1]); + if(!$renter) continue; + + debuglog("unconfirmed $renter->id $renter->address, $a"); + + $renter->unconfirmed = $a; + $renter->updated = time(); + $renter->save(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + $received1 = $remote->getbalance('bittrex', 1); //nicehash payments + if($received1>0) + { + $moved = $remote->move('bittrex', '', $received1); + debuglog("moved from bittrex $received1"); + + dborun("update renters set balance=balance+$received1 where id=7"); + dborun("update renters set custom_start=custom_start+$received1 where id=7"); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + $fees = 0.0001; + + $list = getdbolist('db_rentertxs', "type='withdraw' and tx='scheduled'"); + foreach($list as $tx) + { + $renter = getdbo('db_renters', $tx->renterid); + if(!$renter) continue; + +// debuglog("$renter->balance < $tx->amount + $fees"); + $tx->amount = bitcoinvaluetoa(min($tx->amount, $renter->balance-$fees)); + if($tx->amount < $fees*2) + { + $tx->tx = 'failed'; + $tx->save(); + + continue; + } + + debuglog("withdraw send $renter->id $renter->address sendtoaddress($tx->address, $tx->amount)"); + $tx->tx = $remote->sendtoaddress($tx->address, round($tx->amount, 8)); + + if(!$tx->tx) + { + $tx->tx = 'failed'; + $tx->save(); + + continue; + } + + $renter->balance -= $tx->amount+$fees; + $renter->balance = max($renter->balance, 0); + + dborun("update renters set balance=$renter->balance where id=$renter->id"); + + $tx->save(); + + if($renter->balance <= 0.0001) + dborun("update jobs set active=false, ready=false where id=$renter->id"); + } + +} + + + + + + + diff --git a/web/yaamp/core/backend/sell.php b/web/yaamp/core/backend/sell.php new file mode 100644 index 0000000..14b17a5 --- /dev/null +++ b/web/yaamp/core/backend/sell.php @@ -0,0 +1,126 @@ +0 and symbol!='BTC'"); + foreach($coins as $coin) sellCoinToExchange($coin); +} + +function sellCoinToExchange($coin) +{ + if($coin->dontsell) return; + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $info = $remote->getinfo(); + if(!$info || !$info['balance']) return false; + + if(!empty($coin->symbol2)) + { + $coin2 = getdbosql('db_coins', "symbol='$coin->symbol2'"); + if(!$coin2) return; + + $amount = $info['balance'] - $info['paytxfee']; + $amount *= 0.9; + +// debuglog("sending $amount $coin->symbol to main wallet"); + + $tx = $remote->sendtoaddress($coin2->master_wallet, $amount); +// if(!$tx) debuglog($remote->error); + + return; + } + + $market = getBestMarket($coin); + if(!$market) return; + + if(!$coin->sellonbid && $market->lastsent != null && $market->lastsent > $market->lasttraded) + { +// debuglog("*** not sending $coin->name to $market->name. last tx is late ***"); + return; + } + + $deposit_address = $market->deposit_address; + $marketname = $market->name; + + if(empty($deposit_address)) return false; + $reserved1 = dboscalar("select sum(balance) from accounts where coinid=$coin->id"); + $reserved2 = dboscalar("select sum(amount*price) from earnings + where status!=2 and userid in (select id from accounts where coinid=$coin->id)"); + + $reserved = ($reserved1 + $reserved2) * 10; + $amount = $info['balance'] - $info['paytxfee'] - $reserved; + +// if($reserved>0) +// { +// debuglog("$reserved1 $reserved2 out of {$info['balance']}"); +// debuglog("reserving $reserved $coin->symbol out of $coin->balance, available $amount"); +// } + + if($amount < $coin->reward/4) + { + // debuglog("not enough $coin->symbol to sell $amount < $coin->reward /4"); + return false; + } + + $deposit_info = $remote->validateaddress($deposit_address); + if(!$deposit_info || !isset($deposit_info['isvalid']) || !$deposit_info['isvalid']) + { + debuglog("sell invalid address $deposit_address"); + return; + } + + $amount = round($amount, 8); +// debuglog("sending $amount $coin->symbol to $marketname, $deposit_address"); + + $market->lastsent = time(); + $market->save(); + +// sleep(1); + + $tx = $remote->sendtoaddress($deposit_address, $amount); + if(!$tx) + { + // debuglog($remote->error); + + if($coin->symbol == 'MUE') + $amount = min($amount, 5000); + else if($coin->symbol == 'DIME') + $amount = min($amount, 10000000); + else if($coin->symbol == 'CNOTE') + $amount = min($amount, 10000); + else if($coin->symbol == 'SRC') + $amount = min($amount, 500); + else + $amount = round($amount * 0.99, 8); + +// debuglog("sending $amount $coin->symbol to $deposit_address"); + sleep(1); + + $tx = $remote->sendtoaddress($deposit_address, $amount); + if(!$tx) + { + debuglog("sending $amount $coin->symbol to $deposit_address"); + debuglog($remote->error); + return; + } + } + + $exchange = new db_exchange; + $exchange->market = $marketname; + $exchange->coinid = $coin->id; + $exchange->send_time = time(); + $exchange->quantity = $amount; + $exchange->price_estimate = $coin->price; + $exchange->status = 'waiting'; + $exchange->tx = $tx; + $exchange->save(); + + return; +} + + diff --git a/web/yaamp/core/backend/services.php b/web/yaamp/core/backend/services.php new file mode 100644 index 0000000..5c3d348 --- /dev/null +++ b/web/yaamp/core/backend/services.php @@ -0,0 +1,210 @@ +'scrypt', + 1=>'sha256', + 2=>'scryptn', + 3=>'x11', + 4=>'x13', + 6=>'x15', + 7=>'nist5', + 8=>'neoscrypt', + 9=>'lyra2', + 11=>'qubit', + 12=>'quark', + ); + + $res = fetch_url('https://www.nicehash.com/api?method=stats.global.current'); + if(!$res) return; + + $a = json_decode($res); + if(!$a || !isset($a->result)) return; + + foreach($a->result->stats as $stat) + { + if($stat->price <= 0) continue; + if(!isset($table[$stat->algo])) continue; + $algo = $table[$stat->algo]; + + $service = getdbosql('db_services', "name='Nicehash' and algo=:algo", array(':algo'=>$algo)); + if(!$service) + { + $service = new db_services; + $service->name = 'Nicehash'; + $service->algo = $algo; + } + + $service->price = $stat->price/1000; + $service->speed = $stat->speed*1000000000; + $service->save(); + + $list = getdbolist('db_jobs', "percent>0 and algo=:algo and (host='stratum.westhash.com' or host='stratum.nicehash.com')", array(':algo'=>$algo)); + foreach($list as $job) + { + $job->price = round($service->price*1000*(100-$job->percent)/100, 2); + $job->save(); + } + } + + $list = getdbolist('db_renters', "custom_address is not null and custom_server is not null"); + foreach($list as $renter) + { + $res = fetch_url("https://$renter->custom_server/api?method=stats.provider&addr=$renter->custom_address"); + if(!$res) continue; + + $renter->custom_balance = 0; + $renter->custom_accept = 0; + $renter->custom_reject = 0; + + $a = json_decode($res); + foreach($a->result->stats as $stat) + { + if(!isset($table[$stat->algo])) continue; + $algo = $table[$stat->algo]; + + $renter->custom_balance += $stat->balance; + $renter->custom_accept += $stat->accepted_speed*1000000000; + $renter->custom_reject += $stat->rejected_speed*1000000000; + } + + $renter->save(); + } + + /////////////////////////////////////////////////////////////////////////// + + // renting from nicehash + if(!YAAMP_PRODUCTION) return; +return; + + $apikey = 'c9534a11-0e4e-4d00-be64-a00e34cd927a'; + $apiid = '7215'; + + $deposit = '1C23KmLeCaQSLLyKVykHEUse1R7jRDv9j9'; + $amount = '0.01'; + + $res = fetch_url("https://www.nicehash.com/api?method=balance&id=$apiid&key=$apikey"); + $a = json_decode($res); + $balance = $a->result->balance_confirmed; + + foreach($table as $i=>$algo) + { + $nicehash = getdbosql('db_nicehash', "algo=:algo", array(':algo'=>$algo)); + if(!$nicehash) + { + $nicehash = new db_nicehash; + $nicehash->active = false; + $nicehash->algo = $algo; + } + + if(!$nicehash->active) + { + if($nicehash->orderid) + { + $res = fetch_url("https://www.nicehash.com/api?method=orders.remove&id=$apiid&key=$apikey&algo=$i&order=$nicehash->orderid"); + debuglog($res); + + $nicehash->orderid = null; + } + + $nicehash->btc = null; + $nicehash->price = null; + $nicehash->speed = null; + $nicehash->last_decrease = null; + + $nicehash->save(); + continue; + } + + $price = dboscalar("select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $minprice = $price*0.5; + $setprice = $price*0.7; + $maxprice = $price*0.9; + $cancelprice = $price*1.1; + + $res = fetch_url("https://www.nicehash.com/api?method=orders.get&my&id=$apiid&key=$apikey&algo=$i"); + if(!$res) break; + + $a = json_decode($res); + if(count($a->result->orders) == 0) + { + if($balance < $amount) continue; + $port = getAlgoPort($algo); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.create&id=$apiid&key=$apikey&algo=$i&amount=$amount&price=$setprice&limit=0&pool_host=yaamp.com&pool_port=$port&pool_user=$deposit&pool_pass=xx"); + debuglog($res); + + $nicehash->last_decrease = time(); + $nicehash->save(); + + continue; + } + + $order = $a->result->orders[0]; + debuglog("$algo $order->price $minprice $setprice $maxprice $cancelprice"); + + $nicehash->orderid = $order->id; + $nicehash->btc = $order->btc_avail; + $nicehash->workers = $order->workers; + $nicehash->price = $order->price; + $nicehash->speed = $order->limit_speed; + $nicehash->accepted = $order->accepted_speed; + $nicehash->rejected = $order->rejected_speed; + + if($order->price > $cancelprice && $order->workers > 0) + { + debuglog("* cancel order $algo"); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.remove&id=$apiid&key=$apikey&algo=$i&order=$order->id"); + debuglog($res); + } + + else if($order->price > $maxprice && $order->limit_speed == 0) + { + debuglog("* decrease speed $algo"); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.set.limit&id=$apiid&key=$apikey&algo=$i&order=$order->id&limit=0.05"); + debuglog($res); + } + + else if($order->price > $maxprice && $nicehash->last_decrease+10*60 < time()) + { + debuglog("* decrease price $algo"); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.set.price.decrease&id=$apiid&key=$apikey&algo=$i&order=$order->id"); + debuglog($res); + + $nicehash->last_decrease = time(); + } + + else if($order->price < $minprice && $order->workers <= 0) + { + debuglog("* increase price $algo"); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.set.price&id=$apiid&key=$apikey&algo=$i&order=$order->id&price=$setprice"); + debuglog($res); + } + + else if($order->price < $maxprice && $order->limit_speed == 0.05) + { + debuglog("* increase speed $algo"); + + $res = fetch_url("https://www.nicehash.com/api?method=orders.set.limit&id=$apiid&key=$apikey&algo=$i&order=$order->id&limit=0"); + debuglog($res); + } + + $nicehash->save(); + } + +} + + + + + + + + diff --git a/web/yaamp/core/backend/stats.php b/web/yaamp/core/backend/stats.php new file mode 100644 index 0000000..4e6171d --- /dev/null +++ b/web/yaamp/core/backend/stats.php @@ -0,0 +1,347 @@ +algo terminated"); + $errors .= "$stratum->algo, "; + } + + if(!empty($errors)) + send_email_alert('stratums', "stratums restarted $errors", "stratums were restarted: $errors"); + + dborun("delete from stratums where time<$t"); + dborun("delete from workers where pid not in (select pid from stratums)"); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // long term stats + + $t = floor(time()/60/60)*60*60; + foreach(yaamp_get_algos() as $algo) + { + $pool_rate = yaamp_pool_rate($algo); + + $stats = getdbosql('db_hashstats', "time=$t and algo=:algo", array(':algo'=>$algo)); + if(!$stats) + { + $stats = new db_hashstats; + $stats->time = $t; + $stats->hashrate = $pool_rate; + $stats->algo = $algo; + } + else + { + $percent = 1; + $stats->hashrate = round(($stats->hashrate*(100-$percent) + $pool_rate*$percent) / 100); + } + + $stats->earnings = dboscalar("select sum(amount*price) from blocks where time>$t and category!='orphan' and algo=:algo", array(':algo'=>$algo)); + $stats->save(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // short term stats + + $step = 15; + $t = floor(time()/$step/60)*$step*60; + + foreach(yaamp_get_algos() as $algo) + { + $stats = getdbosql('db_hashrate', "time=$t and algo=:algo", array(':algo'=>$algo)); + if(!$stats) + { + $stats = new db_hashrate; + $stats->time = $t; + $stats->hashrate = dboscalar("select hashrate from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $stats->hashrate_bad = 0; //dboscalar("select hashrate_bad from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $stats->price = dboscalar("select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $stats->rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $stats->algo = $algo; + } + + $pool_rate = yaamp_pool_rate($algo); + $stats->hashrate = $pool_rate; //round(($stats->hashrate*(100-$percent) + $pool_rate*$percent) / 100); + + $pool_rate_bad = yaamp_pool_rate_bad($algo); + $stats->hashrate_bad = $pool_rate_bad; //round(($stats->hashrate_bad*(100-$percent) + $pool_rate_bad*$percent) / 100); + + if($stats->hashrate < 1000) $stats->hashrate = 0; + + $t1 = time() - 5*60; + $total_rentable = dboscalar("select sum(difficulty) from shares where valid and extranonce1 and algo=:algo and time>$t1", array(':algo'=>$algo)); + $total_diff = dboscalar("select sum(difficulty) from shares where valid and algo=:algo and time>$t1", array(':algo'=>$algo)); + $total_rented = 0; + + if(!$total_diff) + { + $t1 = time() - 15*60; + $total_diff = dboscalar("select sum(difficulty) from shares where valid and algo=:algo and time>$t1", array(':algo'=>$algo)); + } + + if($total_diff>0) + { + $price = 0; + $rent = 0; + + $list = dbolist("select coinid, sum(difficulty) as d from shares where valid and algo=:algo and time>$t1 group by coinid", array(':algo'=>$algo)); + foreach($list as $item) + { + if($item['coinid'] == 0) + { + if(!$total_rentable) continue; + $total_rented = $item['d']; + + $price += $stats->rent * $item['d'] / $total_diff; + $rent += $stats->rent * $item['d'] / $total_rentable; + } + else + { + $coin = getdbo('db_coins', $item['coinid']); + if(!$coin) continue; + + $btcghd = yaamp_profitability($coin); + + $price += $btcghd * $item['d'] / $total_diff; + $rent += $btcghd * $item['d'] / $total_diff; + } + } + + $percent = 33; + $rent = max($price, ($stats->rent*(100-$percent) + $rent*$percent) / 100); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + + $aa = $total_rentable * $target / $interval / 1000; + $bb = dboscalar("select sum(speed) from jobs where active and ready and price>$rent and algo=:algo", array(':algo'=>$algo)); + + if($total_rented*1.3 < $total_rentable || $bb > $aa) + $rent += $price*YAAMP_FEES_RENTING/100; + + else + $rent -= $price*YAAMP_FEES_RENTING/100; + + $stats->price = $price; + $stats->rent = $rent; + } + + else + { + $coin = getdbosql('db_coins', "enable and auto_ready and algo=:algo order by index_avg desc", array(':algo'=>$algo)); + if($coin) + { + $btcghd = yaamp_profitability($coin); + $stats->price = $btcghd; + $stats->rent = $stats->price + $stats->price * YAAMP_FEES_RENTING / 100; + } + } + + if(YAAMP_LIMIT_ESTIMATE) + { + $t1 = time() - 24*60*60; + $avg = dboscalar("select avg(price) from hashrate where time>$t1 and algo=:algo", array(':algo'=>$algo)); + if($avg) $stats->price = min($stats->price, $avg*1.5); + } + + $stats->difficulty = dboscalar("select sum(difficulty) from coins where enable and auto_ready and algo=:algo", array(':algo'=>$algo)); + $stats->save(); + } + + ////////////////////////////////////////////////////////////// + + $step = 15; + $t = floor(time()/$step/60)*$step*60; + + $btc = getdbosql('db_coins', "symbol='BTC'"); + $topay = dboscalar("select sum(balance) from accounts where coinid=$btc->id"); //here: take other currencies too + $margin = $btc->balance - $topay; + + $balances = dboscalar("select sum(balance) from balances"); + $onsell = dboscalar("select sum(amount*bid) from orders"); + + $immature = dboscalar("select sum(amount*price) from earnings where status=0"); + $confirmed = dboscalar("select sum(amount*price) from earnings where status=1"); + + $wallets = dboscalar("select sum(balance*price) from coins where enable and symbol!='BTC'"); + $renters = dboscalar("select sum(balance) from renters"); + + $mints = dboscalar("select sum(mint*price) from coins where enable"); + $off = $mints-$immature; + +// debuglog("mint $mints $immature $off"); + + $total_profit = $btc->balance + $balances + $onsell + $wallets - $topay - $renters; + + $stats = getdbosql('db_stats', "time=$t"); + if(!$stats) + { + $stats = new db_stats; + $stats->time = $t; + } + + $stats->profit = $total_profit; + $stats->wallet = $btc->balance; + $stats->wallets = $wallets; + + $stats->margin = $margin; + $stats->balances = $balances; + $stats->onsell = $onsell; + + $stats->immature = $immature; + $stats->waiting = $confirmed; + $stats->renters = $renters; + + $stats->save(); + + ///////////////////////////////////////////////////////////////////////////// + + foreach(yaamp_get_algos() as $algo) + { + $factor = yaamp_get_algo_norm($algo); + + $dbalgo = getdbosql('db_algos', "name='$algo'"); + if(!$dbalgo) + { + $dbalgo = new db_algos; + $dbalgo->name = $algo; + } + + $dbalgo->profit = dboscalar("select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $dbalgo->rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $dbalgo->factor = $factor; + $dbalgo->save(); + } + +// $d1 = microtime(true) - $t1xx; +// controller()->memcache->add_monitoring_function(__METHOD__, $d1); +} + + +function BackendStatsUpdate2() +{ +// debuglog('----------------------------------'); +// debuglog(__FUNCTION__); + + //////////////////////////////////////////////////////////////////////////////////////////////////// + + $step = 15; + $t = floor(time()/$step/60)*$step*60; + + $list = dbolist("select userid, algo from shares where time>$t group by userid, algo"); + foreach($list as $item) + { + $stats = getdbosql('db_hashuser', "time=$t and algo=:algo and userid=:userid", + array(':algo'=>$item['algo'], ':userid'=>$item['userid'])); + if(!$stats) + { + $stats = new db_hashuser; + $stats->userid = $item['userid']; + $stats->time = $t; + $stats->hashrate = dboscalar("select hashrate from hashuser where algo=:algo and userid=:userid order by time desc limit 1", + array(':algo'=>$item['algo'], ':userid'=>$item['userid'])); + $stats->hashrate_bad = 0; + $stats->algo = $item['algo']; + } + + $percent = 20; + $user_rate = yaamp_user_rate($item['userid'], $item['algo']); + + $stats->hashrate = round(($stats->hashrate*(100-$percent) + $user_rate*$percent) / 100); + if($stats->hashrate < 1000) $stats->hashrate = 0; + + $user_rate_bad = yaamp_user_rate_bad($item['userid'], $item['algo']); + + $stats->hashrate_bad = round(($stats->hashrate_bad*(100-$percent) + $user_rate_bad*$percent) / 100); + if($stats->hashrate_bad < 1000) $stats->hashrate_bad = 0; + + $stats->save(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + + $step = 15; + $t = floor(time()/$step/60)*$step*60; + + $list = dbolist("select distinct jobid from jobsubmits where time>$t"); + foreach($list as $item) + { + $jobid = $item['jobid']; + + $stats = getdbosql('db_hashrenter', "time=$t and jobid=$jobid"); + if(!$stats) + { + $stats = new db_hashrenter; + // $stats->renterid = ; + $stats->jobid = $item['jobid']; + $stats->time = $t; + $stats->hashrate = dboscalar("select hashrate from hashrenter where jobid=:jobid order by time desc limit 1", array(':jobid'=>$jobid)); + $stats->hashrate_bad = 0; //dboscalar("select hashrate_bad from hashrenter where jobid=$jobid order by time desc limit 1"); + } + + $percent = 20; + $job_rate = yaamp_job_rate($jobid); + + $stats->hashrate = round(($stats->hashrate*(100-$percent) + $job_rate*$percent) / 100); + if($stats->hashrate < 1000) $stats->hashrate = 0; + + $job_rate_bad = yaamp_job_rate_bad($jobid); + + $stats->hashrate_bad = round(($stats->hashrate_bad*(100-$percent) + $job_rate_bad*$percent) / 100); + if($stats->hashrate_bad < 1000) $stats->hashrate_bad = 0; + + $stats->save(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + + $t = floor(time()/$step/60)*$step*60; + $d = time()-24*60*60; + + $list = getdbolist('db_accounts', "balance>0 or last_login>$d"); + foreach($list as $user) + { + $stats = getdbosql('db_balanceuser', "time=$t and userid=$user->id"); + if(!$stats) + { + $stats = new db_balanceuser; + $stats->userid = $user->id; + $stats->time = $t; + } + +// $refcoin = getdbo('db_coins', $user->coinid); +// if(!$refcoin) $refcoin = getdbosql('db_coins', "symbol='BTC'"); +// if(!$refcoin->price || !$refcoin->price2) continue; + +// $pending1 = dboscalar("select sum(amount*price) from earnings where coinid=$refcoin->id and status!=2 and userid=$user->id"); +// $pending2 = dboscalar("select sum(amount*price) from earnings where coinid!=$refcoin->id and status!=2 and userid=$user->id"); + + $stats->pending = yaamp_convert_earnings_user($user, "status!=2"); + $stats->pending = bitcoinvaluetoa($stats->pending); + + $stats->balance = $user->balance; + $stats->save(); + + $id = dboscalar("select id from earnings where userid=$user->id order by id desc limit 100, 1"); + if($id) dborun("delete from earnings where status=2 and userid=$user->id and id<$id"); + } + + +} + + + + + + + + + diff --git a/web/yaamp/core/backend/system.php b/web/yaamp/core/backend/system.php new file mode 100644 index 0000000..7b8db09 --- /dev/null +++ b/web/yaamp/core/backend/system.php @@ -0,0 +1,253 @@ + $filename"); +} + +function BackendQuickClean() +{ + $delay = time() - 24*60*60; + $coins = getdbolist('db_coins', "installed"); + + foreach($coins as $coin) + { + $id = dboscalar("select id from blocks where coin_id=$coin->id and time<$delay and + id not in (select blockid from earnings where coinid=$coin->id) + order by id desc limit 200, 1"); + + if($id) dborun("delete from blocks where coin_id=$coin->id and time<$delay and + id not in (select blockid from earnings where coinid=$coin->id) and id<$id"); + } + + dborun("delete from earnings where blockid in (select id from blocks where category='orphan')"); + dborun("delete from earnings where blockid not in (select id from blocks)"); +} + +function BackendCleanDatabase() +{ + $delay = time() - 60*24*60*60; +// dborun("delete from blocks where time<$delay"); + dborun("delete from hashstats where time<$delay"); + dborun("delete from payouts where time<$delay"); + dborun("delete from rentertxs where time<$delay"); + + $delay = time() - 2*24*60*60; + dborun("delete from stats where time<$delay"); + dborun("delete from hashrate where time<$delay"); + dborun("delete from hashuser where time<$delay"); + dborun("delete from hashrenter where time<$delay"); + dborun("delete from balanceuser where time<$delay"); + dborun("delete from exchange where send_time<$delay"); + + $delay = time() - 12*60*60; + dborun("delete from earnings where status=2 and mature_time<$delay"); +} + +function BackendOptimizeTables() +{ + $list = dbolist("show tables"); + foreach($list as $item) + { + $tablename = $item['Tables_in_yaamp']; + dbolist("optimize table $tablename"); + + sleep(1); + } +} + +function BackendProcessList() +{ + $list = dbolist("show processlist"); + foreach($list as $item) + { + $conn = getdbo('db_connections', $item['Id']); + if(!$conn) + { + $conn = new db_connections; + $conn->id = $item['Id']; + $conn->user = $item['User']; + $conn->host = $item['Host']; + $conn->db = $item['db']; + $conn->created = time(); + } + + $conn->idle = $item['Time']; + $conn->last = time(); + + $conn->save(); + } + + $delay = time() - 5*60; + dborun("delete from connections where last<$delay"); +} + +function BackendRunCoinActions() +{ +// debuglog(__FUNCTION__); + +// $hostname = exec("hostname"); +// $server = getdbosql('db_servers', "name='$hostname'"); +// if(!$server) +// { +// $server = new db_servers; +// $server->name = $hostname; +// } + +// $server->uptime = exec("uptime"); +// $server->save(); + + ////////////////////////////////////////////////////////////////////////// + +// if($hostname == 'yaamp') +// { +// $mining = getdbosql('db_mining'); +// exec("pgrep stratum", $ids, $ret); + +// $mining->stratumids = implode(',', $ids); +// $mining->save(); +// } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + +// $coins = getdbolist('db_coins'); +// foreach($coins as $coin) +// { +// if(empty($coin->action) || empty($coin->program) || empty($coin->conf_folder)) continue; +// $hostname = exec("hostname"); +// if($hostname != $coin->rpchost) continue; +// switch($coin->action) +// { +// case 1: +// debuglog("starting $coin->program"); + +// $debugfile = YAAMP_WALLETS."/$coin->conf_folder/debug.log"; +// @unlink($debugfile); + +// exec(YAAMP_BIN."/run-background.sh ".YAAMP_BIN."/$coin->program"); +// $coin->enable = true; +// break; + +// case 2: +// debuglog("stopping $coin->program"); +// exec(YAAMP_BIN."/$coin->program stop"); +// break; + +// case 4: +// debuglog("restarting $coin->program"); +// exec(YAAMP_BIN."/$coin->program stop"); +// sleep(10); + +// $debugfile = YAAMP_WALLETS."/$coin->conf_folder/debug.log"; +// @unlink($debugfile); + +// exec(YAAMP_BIN."/run-background.sh ".YAAMP_BIN."/$coin->program"); +// $coin->enable = true; +// break; + +// case 3: +// debuglog("reset blockchain $coin->conf_folder"); +// $folder = YAAMP_WALLETS."/$coin->conf_folder"; + +// // exec("rm {$folder}/blk*.dat"); +// exec("rm -fr {$folder}/database"); +// break; + +// case 5: +// $t = time(); +// $coin->rpcport = $coin->id*2+10240; +// $coin->rpcuser = 'yaamprpc'; +// $coin->rpcpasswd = md5("$t.$coin->id"); +// $coin->save(); + +// $configfile = YAAMP_WALLETS."/$coin->conf_folder/".substr($coin->conf_folder, 1).".conf"; +// debuglog("make config $configfile"); + +// $stratum_port = 3433; + +// if($coin->algo == 'sha256') +// $stratum_port = 3333; + +// else if($coin->algo == 'scrypt') +// $stratum_port = 3433; + +// else if($coin->algo == 'x11') +// $stratum_port = 3533; + +// else if($coin->algo == 'x13') +// $stratum_port = 3633; + +// else if($coin->algo == 'x14') +// $stratum_port = 3933; + +// else if($coin->algo == 'x15') +// $stratum_port = 3733; + +// else if($coin->algo == 'nist5') +// $stratum_port = 3833; + +// else if($coin->algo == 'quark') +// $stratum_port = 4033; + +// else if($coin->algo == 'fresh') +// $stratum_port = 4133; + +// else if($coin->algo == 'neoscrypt') +// $stratum_port = 4233; + +// else if($coin->algo == 'scryptn') +// $stratum_port = 4333; + +// else if($coin->algo == 'lyra2') +// $stratum_port = 4433; + +// else if($coin->algo == 'blake') +// $stratum_port = 4533; + +// // else if($coin->algo == 'keccak') +// // $stratum_port = 3933; + +// $data = "server=1\ndeamon=1\ngen=0\nrpcuser=$coin->rpcuser\nrpcpassword=$coin->rpcpasswd\nrpcport=$coin->rpcport\nrpcallowip=10.*.*.*\nblocknotify=".YAAMP_BIN."/blocknotify.sh --host yaamp --port $stratum_port --id $coin->id --block %s --password tu8tu5\n"; +// file_put_contents($configfile, $data); + +// debuglog("starting $coin->program"); + +// $debugfile = YAAMP_WALLETS."/$coin->conf_folder/debug.log"; +// @unlink($debugfile); + +// exec(YAAMP_BIN."/run-background.sh ".YAAMP_BIN."/$coin->program"); +// $coin->enable = true; + +// break; +// } + +// $coin->action = null; +// $coin->save(); +// } + +// $filename = '/root/stratum/stratum.log'; +// $filesize = filesize($filename); + +// $oldsize = controller()->memcache->get('stratum_log_size'); +// if($oldsize == $filesize) return; + +// $file = fopen($filename, $r); +// if(!$file) return; + +// fseek($file, $oldsize); +// $data = fread($file, $filesize-$oldsize); +// fclose($file); + +// controller()->memcache->set('stratum_log_size', $filesize); +// system("echo \"$data\" | mail -s \"yaamp server\" yaamp201@gmail.com"); +} + + + + + + + diff --git a/web/yaamp/core/backend/users.php b/web/yaamp/core/backend/users.php new file mode 100644 index 0000000..dc926fe --- /dev/null +++ b/web/yaamp/core/backend/users.php @@ -0,0 +1,95 @@ +username, $user->coinsymbol"); + if(!empty($user->coinsymbol)) + { + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$user->coinsymbol)); + $user->coinsymbol = ''; + + if($coin) + { + if($user->coinid == $coin->id) + { + $user->save(); + continue; + } + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $b = $remote->validateaddress($user->username); + if($b && isset($b['isvalid']) && $b['isvalid']) + { + if($user->balance > 0) + { + $coinref = getdbo('db_coins', $user->coinid); + if(!$coinref) $coinref = getdbosql('db_coins', "symbol='BTC'"); + + $user->balance = $user->balance*$coinref->price/$coin->price; + } + + $user->coinid = $coin->id; + $user->save(); + + debuglog("$user->username set to $coin->symbol, balance $user->balance"); + continue; + } + } + } + + $user->coinid = 0; + + $coins = getdbolist('db_coins', "enable order by difficulty desc"); + foreach($coins as $coin) + { + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $b = $remote->validateaddress($user->username); + if(!$b || !isset($b['isvalid']) || !$b['isvalid']) continue; + + $user->balance = 0; + $user->coinid = $coin->id; + + debuglog("$user->username set to $coin->symbol, balance $user->balance"); + break; + } + + $user->save(); + } + +// $delay=time()-60*60; +// $list = dborun("update coins set dontsell=1 where id in (select coinid from accounts where balance>0 or last_login>$delay group by coinid)"); +// $list = dborun("update coins set dontsell=0 where id not in (select coinid from accounts where balance>0 or last_login>$delay group by coinid)"); + + +// $list = getdbolist('db_workers', "dns is null"); +// foreach($list as $worker) +// { +// $worker->dns = $worker->ip; +// $res = system("resolveip $worker->ip"); + +// if($res) +// { +// $a = explode(' ', $res); +// if($a && isset($a[5])) +// $worker->dns = $a[5]; +// } + +// $worker->save(); +// } + + $d1 = microtime(true) - $t1; + controller()->memcache->add_monitoring_function(__METHOD__, $d1); +} + + + + + + diff --git a/web/yaamp/core/common/Shortcuts.php b/web/yaamp/core/common/Shortcuts.php new file mode 100644 index 0000000..fc6e4bc --- /dev/null +++ b/web/yaamp/core/common/Shortcuts.php @@ -0,0 +1,77 @@ +clientScript; +} + +function url($route,$params=array(),$ampersand='&') +{ + return Yii::app()->createUrl($route,$params,$ampersand); +} + +/////////////////////////////////////////////////////////////////////// + +function l($text, $url = '#', $htmlOptions = array()) +{ + return CHtml::link($text, $url, $htmlOptions); +} + +function img($text, $url = '#', $htmlOptions = array()) +{ + return CHtml::image($text, $url, $htmlOptions); +} + +function t($message, $category = 'stay', $params = array(), $source = null, $language = null) +{ + return Yii::t($category, $message, $params, $source, $language); +} + +function bu($url=null) +{ + static $baseUrl; + if ($baseUrl===null) + $baseUrl=Yii::app()->request->baseUrl; + return $url===null ? $baseUrl : $baseUrl.'/'.ltrim($url,'/'); +} + +function tf($url = null) +{ + error_log("tf($url) called"); + return ''; +} + +function user() +{ + return Yii::app()->user; +} + +function now() +{ + return date("Y-m-d H:i:s"); +} + +///////////////////////////////////////////////////////////// + +function JavascriptFile($filename) +{ + echo CHtml::scriptFile($filename); +} + +function Javascript($javascript) +{ + echo ""; +} + +function JavascriptReady($javascript) +{ + echo ""; +} + + + diff --git a/web/yaamp/core/common/common.php b/web/yaamp/core/common/common.php new file mode 100644 index 0000000..fe894b7 --- /dev/null +++ b/web/yaamp/core/common/common.php @@ -0,0 +1,10 @@ += 1) $text = $text.' ====================='; + error_log("$t, $d - $text"); + + $global_lastlog = $t; +} + +// + +function LimitRequest($name, $limit=1) +{ + $t = controller()->memcache->get("yaamp-timestamp-$name-{$_SERVER['REMOTE_ADDR']}"); + $a = controller()->memcache->get("yaamp-average-$name-{$_SERVER['REMOTE_ADDR']}"); + + if(!$a || !$t) $a = $limit; + + else + { + $p = 33; + $a = ($a * (100-$p) + (microtime(true)-$t) * $p) / 100; + } + + if($a < $limit) return false; + + controller()->memcache->set("yaamp-timestamp-$name-{$_SERVER['REMOTE_ADDR']}", microtime(true), 300); + controller()->memcache->set("yaamp-average-$name-{$_SERVER['REMOTE_ADDR']}", $a, 300); + + return true; +} + +function getuserparam($address) +{ + if(empty($address)) return null; + + $address = substr($address, 0, 34); + $user = getdbosql('db_accounts', "username=:ad", array(':ad'=>$address)); + + return $user; +} + +function getrenterparam($address) +{ + if(empty($address)) return null; + + $address = substr($address, 0, 34); + $renter = getdbosql('db_renters', "address=:ad", array(':ad'=>$address)); + + return $renter; +} + +/////////////////////////////////////////////////////////// + +function GetSSModulePath($name) +{ + $result = findfile('yaamp/models', "/\/{$name}.php/"); + if(!$result) + $result = findfile('yaamp/modules', "/\/{$name}.php/"); + +// debuglog($result); + return $result; +} + +function findfile($path, $pattern) +{ + $result = null; + + $path = rtrim(str_replace("\\", "/", $path), '/') . '/*'; + foreach(glob($path) as $fullname) + { + if(is_dir($fullname)) + { + $result = findfile($fullname, $pattern); + if($result) break; + } + + else if(preg_match($pattern, $fullname)) + { + $result = $fullname; + break; + } + } + + return $result; +} + +function mydump($obj, $level=2) +{ + CVarDumper::dump($obj, $level, true); + echo "
    "; +} + +function mydumperror($obj, $level=2) +{ + CVarDumper::dumperror($obj, $level); +} + +function debuglog($string, $level=2) +{ + if(is_object($string)) + { + mydumperror($string, $level); + return; + } + + if(is_array($string)) + { + mydumperror($string, $level); + return; + } + + $now = now(); + if(!is_dir(YAAMP_LOGS)) mkdir(YAAMP_LOGS); + error_log("[$now] $string\n", 3, YAAMP_LOGS."/debug.log"); +} + +function rentallog($string) +{ + $now = now(); + if(!is_dir(YAAMP_LOGS)) mkdir(YAAMP_LOGS); + error_log("[$now] $string\n", 3, YAAMP_LOGS."/rental.log"); + + debuglog($string); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +function xmltoarray($xmlcontent) +{ + $xml = simplexml_load_string($xmlcontent); + $json = json_encode($xml); + $array = json_decode($json, true); + + return $array; +} + +function XssFilter($data) +{ + $data = str_replace(">", "", $data); + $data = str_replace("<", "", $data); + $data = str_replace("'", "", $data); + $data = str_replace('"', "", $data); +// $data = str_replace(".", "", $data); + $data = str_replace("\\", "", $data); + $data = str_replace("&", "", $data); + $data = str_replace(";", "", $data); + +// mydump($data); die; + return $data; +} + +function showDatetimePicker($model, $attribute, $options='') +{ + $name = "{$model->tableSchema->name}[{$attribute}]"; + $id = "{$model->tableSchema->name}_{$attribute}"; + + echo ""; + + echo ""; +} + +function showDatetimePicker2($name, $value, $options='', $callback='null') +{ + $id = $name; + echo ""; + + if(empty($value)) $value = $name; + echo ""; +} + +function showSubmitButton($name) +{ + echo "
    "; + echo CUFHtml::submitButton($name, array('id'=>'btnSubmit')); + echo "
    "; + echo ""; +} + +function InitMenuTabs($tabname) +{ + JavascriptReady("$('$tabname').tabs({ select: function(event, ui){ + window.location.replace(ui.tab.hash); return true;}});"); +} + +function fetch_url($url) +{ +// debuglog("fetch_url($url)"); + $buffer = ''; + + $file = @fopen($url, "r"); + if(!$file) return null; + + while(!feof($file)) + { + $line = fgets($file, 1024); + $buffer .= $line; + } + + fclose($file); + return $buffer; +} + +function gettempfile($ext) +{ + $phpsessid = session_id(); + $random = mt_rand(); + + $filename = SANSSPACE_TEMP."\\{$phpsessid}-{$random}{$ext}"; + return $filename; +} + +function make_bitly_url($url, $format = 'xml', $version = '2.0.1') +{ + $login = 'o_1uu6u4g2h4'; + $appkey = 'R_433ebafeb24374d6c183c0fcbcc01575'; + + $bitly = 'http://api.bit.ly/shorten?version='.$version.'&longUrl='.urlencode($url).'&login='.$login.'&apiKey='.$appkey.'&format='.$format; + $response = file_get_contents($bitly); +// debuglog($response); + + if(strtolower($format) == 'json') + { + $json = @json_decode($response,true); + return $json['results'][$url]['shortUrl']; + } + else //xml + { + $xml = simplexml_load_string($response); + return 'bit.ly/'.$xml->results->nodeKeyVal->hash; + } +} + +function resolveShortURL($url1) +{ + $ch = curl_init("$url1"); + + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_ENCODING , "deflate,gzip"); + + $http_data = curl_exec($ch); + $curl_info = curl_getinfo($ch); + $headers = substr($http_data, 0, $curl_info["header_size"]); + + preg_match("!\r\n(?:Location|URI): *(.*?) *\r\n!", $headers, $matches); + $url = $matches[1]; + +// debuglog(" short $url1 -> $url"); + return empty($url)? $url1: $url; +} + +function is_short_url($url) +{ + // 1. Overall URL length - May be a max of 30 charecters + if (strlen($url) > 30) return false; + + $parts = parse_url($url); + + // No query string & no fragment + if ($parts["query"] || $parts["fragment"]) return false; + + $path = $parts["path"]; + $pathParts = explode("/", $path); + + // 3. Number of '/' after protocol (http://) - Max 2 + if (count($pathParts) > 2) return false; + + // 2. URL length after last '/' - May be a max of 10 characters + $lastPath = array_pop($pathParts); + if (strlen($lastPath) > 12) return false; + + // 4. Max length of host + if (strlen($parts["host"]) > 10) return false; + + return true; +} diff --git a/web/yaamp/core/common/libdbo.php b/web/yaamp/core/common/libdbo.php new file mode 100644 index 0000000..e83f097 --- /dev/null +++ b/web/yaamp/core/common/libdbo.php @@ -0,0 +1,91 @@ +getTableSchema(); + + $sql = "$table->primaryKey=:db_key"; + return $record->find($sql, array(':db_key'=>$id)); + +// return $record->findByPk($id); +} + +function getdbosql($class, $sql='1', $params=array()) +{ +// debuglog("$class, $sql"); + return CActiveRecord::model($class)->find($sql, $params); +} + +function getdbolist($class, $sql='1', $params=array()) +{ +// debuglog("sql $sql"); + return CActiveRecord::model($class)->findAll($sql, $params); +} + +function getdbocount($class, $sql='1', $params=array()) +{ +// debuglog("sql $sql"); + return CActiveRecord::model($class)->count($sql, $params); +} + +function dborun($sql, $params=array()) +{ + $command = app()->db->createCommand($sql); + + foreach($params as $name=>$value) + $command->bindValue($name, $value); + + return $command->execute(); +} + +function dboscalar($sql, $params=array()) +{ + $command = app()->db->createCommand($sql); + + foreach($params as $name=>$value) + $command->bindValue($name, $value); + + return $command->queryScalar(); +} + +function dborow($sql, $params=array()) +{ + $command = app()->db->createCommand($sql); + + foreach($params as $name=>$value) + $command->bindValue($name, $value); + + return $command->queryRow(); +} + +function dbocolumn($sql, $params=array()) +{ + $command = app()->db->createCommand($sql); + + foreach($params as $name=>$value) + $command->bindValue($name, $value); + + return $command->queryColumn(); +} + +function dbolist($sql, $params=array()) +{ + $command = app()->db->createCommand($sql); + + foreach($params as $name=>$value) + $command->bindValue($name, $value); + + return $command->queryAll(); +} + + + + + + + + + diff --git a/web/yaamp/core/common/system.php b/web/yaamp/core/common/system.php new file mode 100644 index 0000000..7ece5cb --- /dev/null +++ b/web/yaamp/core/common/system.php @@ -0,0 +1,135 @@ +memcache->memcache, "last_email_sent_$name"); + if($last + $t*60 > time()) return; + + debuglog("mail('yaamp201@gmail.com', $title, ...)"); + + $b = mail('yaamp201@gmail.com', $title, $message); + if(!$b) debuglog('error sending email'); + + memcache_set(controller()->memcache->memcache, "last_email_sent_$name", time()); +} + +function dos_filesize($fn) +{ + return exec('FOR %A IN ("'.$fn.'") DO @ECHO %~zA'); +} + +////////////////////////////////////////////////////////////// + +function GetCpuLoad() +{ + $wmi = new COM("Winmgmts://"); + $result = $wmi->execquery("SELECT LoadPercentage FROM Win32_Processor"); + + $cpu_num = 0; + $load_total = 0; + + foreach($result as $cpu) + { + $cpu_num++; + $load_total += $cpu->loadpercentage; + } + + $load = round($load_total/$cpu_num); + return $load; +} + +function GetNetworkLoad() +{ + $wmi = new COM("Winmgmts://"); + $allnets = $wmi->execquery("Select BytesTotalPersec From Win32_PerfFormattedData_Tcpip_NetworkInterface where BytesTotalPersec>1"); + + $totalbps = 0; + foreach($allnets as $network) + { + $bps = $network->BytesTotalPersec*8; + $totalbps += $bps; + } + + return $totalbps; +} + +function getclientip() +{ + if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) + return $_SERVER['HTTP_X_FORWARDED_FOR']; + + return $_SERVER['REMOTE_ADDR']; +} + +/** + * Retrieves the best guess of the client's actual IP address. + * Takes into account numerous HTTP proxy headers due to variations + * in how different ISPs handle IP addresses in headers between hops. + */ +function get_ip_address() +{ + // check for shared internet/ISP IP + if (!empty($_SERVER['HTTP_CLIENT_IP']) && validate_ip($_SERVER['HTTP_CLIENT_IP'])) { + return $_SERVER['HTTP_CLIENT_IP']; + } + + // check for IPs passing through proxies + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + // check if multiple ips exist in var + if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) { + $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + foreach ($iplist as $ip) { + if (validate_ip($ip)) + return $ip; + } + } else { + if (validate_ip($_SERVER['HTTP_X_FORWARDED_FOR'])) + return $_SERVER['HTTP_X_FORWARDED_FOR']; + } + } + if (!empty($_SERVER['HTTP_X_FORWARDED']) && validate_ip($_SERVER['HTTP_X_FORWARDED'])) + return $_SERVER['HTTP_X_FORWARDED']; + if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP'])) + return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP']; + if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validate_ip($_SERVER['HTTP_FORWARDED_FOR'])) + return $_SERVER['HTTP_FORWARDED_FOR']; + if (!empty($_SERVER['HTTP_FORWARDED']) && validate_ip($_SERVER['HTTP_FORWARDED'])) + return $_SERVER['HTTP_FORWARDED']; + + // return unreliable ip since all else failed + return $_SERVER['REMOTE_ADDR']; +} + +/** + * Ensures an ip address is both a valid IP and does not fall within + * a private network range. + */ +function validate_ip($ip) +{ + if (strtolower($ip) === 'unknown') + return false; + + // generate ipv4 network address + $ip = ip2long($ip); + + // if the ip is set and not equivalent to 255.255.255.255 + if ($ip !== false && $ip !== -1) { + // make sure to get unsigned long representation of ip + // due to discrepancies between 32 and 64 bit OSes and + // signed numbers (ints default to signed in PHP) + $ip = sprintf('%u', $ip); + // do private network range checking + if ($ip >= 0 && $ip <= 50331647) return false; + if ($ip >= 167772160 && $ip <= 184549375) return false; + if ($ip >= 2130706432 && $ip <= 2147483647) return false; + if ($ip >= 2851995648 && $ip <= 2852061183) return false; + if ($ip >= 2886729728 && $ip <= 2887778303) return false; + if ($ip >= 3221225984 && $ip <= 3221226239) return false; + if ($ip >= 3232235520 && $ip <= 3232301055) return false; + if ($ip >= 4294967040) return false; + } + return true; +} + diff --git a/web/yaamp/core/common/util.php b/web/yaamp/core/common/util.php new file mode 100644 index 0000000..7f69085 --- /dev/null +++ b/web/yaamp/core/common/util.php @@ -0,0 +1,317 @@ +getController(); +} + +function getparam($p) +{ + return isset($_REQUEST[$p])? $_REQUEST[$p]: ''; +} + +function getiparam($p) +{ + return isset($_REQUEST[$p])? intval($_REQUEST[$p]): 0; +} + +////////////////////////////////////////////////////// + +function downloadFile($url, &$size) +{ + $data = file_get_contents($url); + $tempname = gettempfile('.ext'); + + file_put_contents($tempname, $data); + $size = dos_filesize($tempname); + + unlink($tempname); + return $data; +} + +function getServerName() +{ + if(strpos($_SERVER['SERVER_NAME'], ':')) + return substr($_SERVER['SERVER_NAME'], 0, strpos($_SERVER['SERVER_NAME'], ':')); + + return $_SERVER['SERVER_NAME']; +} + +function getFullServerName() +{ + if(isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) + $protocol = 'https'; + else + $protocol = 'http'; + + return $protocol.'://'.$_SERVER['HTTP_HOST']; +} + +/////////////////// + +function getClientPlatform() +{ + $agent = $_SERVER['HTTP_USER_AGENT']; + $bname = 'Unknown'; + $platform = 'Unknown'; + $version= ""; + + if(preg_match('/ipad/i', $agent)) + $platform = 'Ipad'; + + else if(preg_match('/iphone/i', $agent)) + $platform = 'Iphone'; + + else if(preg_match('/android/i', $agent)) + $platform = 'Android'; + + else if(preg_match('/linux/i', $agent)) + $platform = 'Linux'; + + elseif(preg_match('/macintosh|mac os x/i', $agent)) + $platform = 'Mac'; + + elseif(preg_match('/windows|win32/i', $agent)) + $platform = 'Windows'; + + ////////////////////////////////////////////////////////////////////// + + if(preg_match('/MSIE/i',$agent) && !preg_match('/Opera/i',$agent)) + { + $bname = 'Internet Explorer'; + $ub = "MSIE"; + } + elseif(preg_match('/Firefox/i',$agent)) + { + $bname = 'Firefox'; + $ub = "Firefox"; + } + elseif(preg_match('/Chrome/i',$agent)) + { + $bname = 'Chrome'; + $ub = "Chrome"; + } + elseif(preg_match('/Safari/i',$agent)) + { + $bname = 'Safari'; + $ub = "Safari"; + } + elseif(preg_match('/Opera/i',$agent)) + { + $bname = 'Opera'; + $ub = "Opera"; + } + elseif(preg_match('/Netscape/i',$agent)) + { + $bname = 'Netscape'; + $ub = "Netscape"; + } + + // finally get the correct version number + $known = array('Version', $ub, 'other'); + $pattern = '#(?' . join('|', $known) . ')[/ ]+(?[0-9.|a-zA-Z.]*)#'; + + preg_match_all($pattern, $agent, $matches); + + // see how many we have + $i = count($matches['browser']); + if ($i != 1) { + //we will have two since we are not using 'other' argument yet + //see if version is before or after the name + if (strripos($agent,"Version") < strripos($agent,$ub)){ + $version= $matches['version'][0]; + } + else { + $version= $matches['version'][1]; + } + } + else { + $version= $matches['version'][0]; + } + + // check if we have a number + if ($version==null || $version=="") {$version="?";} + + return "$platform, $bname $version"; +} + +function IsMobileDevice() +{ + $agent = $_SERVER['HTTP_USER_AGENT']; + + return preg_match('/ipad/i', $agent) || + preg_match('/iphone/i', $agent) || + preg_match('/android/i', $agent); +} + +function file_get_contents_curl($url, $user=null) +{ + $ch = curl_init($url); + + if($user) + { + $a = explode(',', $user->access_token); + $oauth_token = $a[0]; + $oauth_token_secret = $a[1]; + + $oauth = array( + 'oauth_consumer_key' => CONSUMER_KEY, + 'oauth_nonce' => md5(microtime().mt_rand()), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => time(), + 'oauth_token' => $oauth_token, + 'oauth_version' => '1.0'); + + $base_info = buildBaseString($url, 'POST', $oauth); + $composite_key = rawurlencode(CONSUMER_SECRET).'&'.rawurlencode($oauth_token_secret); + $oauth['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true)); + $header = buildAuthorizationHeader($oauth); + + // debuglog($header); + curl_setopt($ch, CURLOPT_HTTPHEADER, array($header)); + } + + $maxallowed = 64*1024; + $totalread = 0; + $data = ''; + + $callback = function($ch, $text) use(&$data, &$maxallowed, &$totalread) + { + $data .= $text; + $count = strlen($text); + $totalread += $count; + + if($totalread >= $maxallowed || stristr($data, '')) + return 0; + + return $count; + }; + + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; Tweetshow 2.1)'); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); + curl_setopt($ch, CURLOPT_TIMEOUT, 20); + curl_setopt($ch, CURLOPT_ENCODING , "deflate,gzip"); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback); + + curl_exec($ch); + curl_close($ch); + +// debuglog(" total read $totalread, ".strlen($data)); + return $data; +} + +function force_wordbreak($text, $max) +{ + $last = 0; + for($i=0; $i $max) + { + $text = substr_replace($text, ' ', $i, 0); + $i++; + $last = $i; + } + } + + return $text; +} + + +///////////////////////////////////////////////////////////////////////// + +function adjust_background_color($color) +{ + sscanf($color, "%x", $rgb); + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + $limit1 = 0xf4; + if($r < $limit1) $r = $limit1-($limit1 - $r)/16; + if($g < $limit1) $g = $limit1-($limit1 - $g)/16; + if($b < $limit1) $b = $limit1-($limit1 - $b)/32; + $color1 = ($r << 16) + ($g << 8) + $b; + $color1 = sprintf("%x", $color1); + return $color1; +} + +function adjust_foreground_color($color) +{ + sscanf($color, "%x", $rgb); + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + $limit2 = 0x33; + if($r > $limit2) $r = $limit2; + if($g > $limit2) $g = $limit2; + if($b > $limit2) $b = $limit2; + $color2 = ($r << 16) + ($g << 8) + $b; + $color2 = sprintf("%x", $color2); + return $color2; +} + +function changeColor($user) +{ + if(!$user) return; + + $color = $user->profile_background_color; + sscanf($color, "%x", $rgb); + + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + + $r = $r * 5/6; + $g = $g * 5/6; + $b = $b * 5/6; + + $color2 = ($r << 16) + ($g << 8) + $b; + $color2 = sprintf("%x", $color2); + + echo "\n"; + echo "\n"; + echo "\n"; + echo "\n"; +} + +class RecursiveDOMIterator implements RecursiveIterator +{ + protected $_position; + protected $_nodeList; + public function __construct(DOMNode $domNode) + { + $this->_position = 0; + $this->_nodeList = $domNode->childNodes; + } + public function getChildren() { return new self($this->current()); } + public function key() { return $this->_position; } + public function next() { $this->_position++; } + public function rewind() { $this->_position = 0; } + public function valid() + { + return $this->_position < $this->_nodeList->length; + } + public function hasChildren() + { + return $this->current()->hasChildNodes(); + } + public function current() + { + return $this->_nodeList->item($this->_position); + } +} + + + + + + + \ No newline at end of file diff --git a/web/yaamp/core/common/utilConversion.php b/web/yaamp/core/common/utilConversion.php new file mode 100644 index 0000000..46ed455 --- /dev/null +++ b/web/yaamp/core/common/utilConversion.php @@ -0,0 +1,296 @@ +> 24) & 0xff; + $d = 0x0000ffff / ($c & 0x00ffffff); + + while ($nShift < 29) + { + $d *= 256.0; + $nShift++; + } + + while ($nShift > 29) + { + $d /= 256.0; + $nShift--; + } + + $v = 0x0000ffff00000000/$d; + return $v; +} + +function htoi($s) +{ + $val = 0.0; + $x = 0; + + if($s[$x] == '0' && ($s[$x+1] == 'x' || $s[$x+1] == 'X')) + $x += 2; + + while(isset($s[$x])) + { +// debuglog("{$s[$x]}"); + + if($s[$x] >= '0' && $s[$x] <='9') + $val = $val * 16 + $s[$x] - '0'; + + else if($s[$x]>='A' && $s[$x] <='F') + { + debuglog($s[$x]); + debuglog($s[$x] - chr('A')); + + $val = $val * 16 + ord($s[$x]) - ord('A') + 10; + } + + else if($s[$x]>='a' && $s[$x] <='f') + $val = $val * 16 + ord($s[$x]) - ord('a') + 10; + + else + return 0; + + $x++; + } + + return $val; +} + +function GetMonthString($n) +{ + $timestamp = mktime(0, 0, 0, $n, 1, 2005); + return date("F", $timestamp); +} + +function bitcoinvaluetoa($v) +{ + return sprintf('%.8f', round($v, 8, PHP_ROUND_HALF_DOWN)); +} + +function mbitcoinvaluetoa($v) +{ + return sprintf('%.4f', round($v, 4, PHP_ROUND_HALF_DOWN)); +} + +function altcoinvaluetoa($v) +{ + return sprintf('%.6f', round($v, 6, PHP_ROUND_HALF_DOWN)); +} + +function datetoa($d) +{ + if(!$d) return ''; + + $t = wp_mktime($d); + $e = time() - $t; + + $table = array( + // limit divider + array('year', 60*60*24*365, 60*60*24*365), + array('month', 60*60*24*60, 60*60*24*30), + array('week', 60*60*24*14, 60*60*24*7), + array('day', 60*60*24*2, 60*60*24), + array('hour', 60*60*2, 60*60), + array('min', 60*2, 60), + array('sec', 0, 1), + ); + + foreach($table as $r) + { + if($e >= $r[1]) + { + $res = floor($e/$r[2]) . " " . $r[0] . (($e/$r[2])>=2?"s":""); //. " ago"; + break; + } + } + + return "$res"; +} + +function datetoa2($d) +{ + if(!$d) return ''; + + $table = array( + // limit divider + array('y', 60*60*24*365, 60*60*24*365), + array('mo', 60*60*24*60, 60*60*24*30), + array('w', 60*60*24*14, 60*60*24*7), + array('d', 60*60*24*2, 60*60*24), + array('h', 60*60*2, 60*60), + array('m', 90, 60), + array('s', 0, 1), + ); + + $e = time() - $d; + foreach($table as $r) + { + if($e >= $r[1]) + { + // $res = floor($e/$r[2]) . " " . $r[0] . (($e/$r[2])>=2?"s":""). " ago"; + // $res = ''.floor($e/$r[2]) . " " . $r[0] . (($e/$r[2])>=2?"s":""); + $res = ''.floor($e/$r[2]) . $r[0]; + break; + } + } + + $f = date('Y-m-d H:i:s', $d); + if(empty($res)) $res = 'now'; + return "$res"; +} + +function sectoa($i) +{ +// if($i < (60*60)) +// return sprintf("%d:%02d", $i%(60*60)/60, $i%60); +// else + return sprintf("%d:%02d:%02d", $i/(60*60), $i%(60*60)/60, $i%60); +} + +function sectoa2($i) +{ + $table = array( + // limit divider + array('year', 60*60*24*365, 60*60*24*365), + array('month', 60*60*24*60, 60*60*24*30), + array('week', 60*60*24*14, 60*60*24*7), + array('day', 60*60*24*2, 60*60*24), + array('hour', 60*60*2, 60*60), + array('min', 60*2, 60), + array('sec', 0, 1), + ); + + $res = ''; + foreach($table as $r) + { + // debuglog("testing {$r[0]}"); + if($i >= $r[1]) + { + $res = floor($i/$r[2]) . " " . $r[0] . (($i/$r[2]) >= 2? "s": ""); + break; + } + } + + return $res; +} + +function Itoa($i) +{ + $s = ''; + if($i >= 1024*1024*1024) + $s = round(floatval($i)/1024/1024/1024, 1) ."G"; + else if($i >= 1024*1024) + $s = round(floatval($i)/1024/1024, 1) ."M"; + else if($i >= 1024) + $s = round(floatval($i)/1024, 1) ."K"; + else + $s = round(floatval($i), 1); + + return $s; +} + +function Itoa2($i, $precision=1) +{ + $s = ''; + if($i >= 1000*1000*1000*1000*1000) + $s = round(floatval($i)/1000/1000/1000/1000/1000, $precision) ." p"; + else if($i >= 1000*1000*1000*1000) + $s = round(floatval($i)/1000/1000/1000/1000, $precision) ." t"; + else if($i >= 1000*1000*1000) + $s = round(floatval($i)/1000/1000/1000, $precision) ." g"; + else if($i >= 1000*1000) + $s = round(floatval($i)/1000/1000, $precision) ." m"; + else if($i >= 1000) + $s = round(floatval($i)/1000, $precision) ." k"; + else + $s = round(floatval($i), $precision); + + return $s; +} + +function YesNo($b) +{ + if($b) return 'Yes'; + else return ''; +} + +function Booltoa($b) +{ + if($b) + return ''; +} + +function precision($n, $p) +{ + if($p < 0) return 0; + + $temp = pow(10, $p); + return round($n * $temp) / $temp; +} + +/////////////////////////////////////// + +function wp_mktime($_timestamp = '') +{ + if($_timestamp){ + $_split_datehour = explode(' ',$_timestamp); + $_split_data = explode("-", $_split_datehour[0]); + $_split_hour = explode(":", $_split_datehour[1]); + + return mktime ($_split_hour[0], $_split_hour[1], $_split_hour[2], $_split_data[1], $_split_data[2], $_split_data[0]); + } +} + +/////////////////////// + +function strip_tags_content($text, $tags = '', $invert = FALSE) +{ + preg_match_all('/<(.+?)[\s]*\/?[\s]*>/si', trim($tags), $tags); + $tags = array_unique($tags[1]); + + if(is_array($tags) AND count($tags) > 0) { + if($invert == FALSE) { + return preg_replace('@<(?!(?:'. implode('|', $tags) .')\b)(\w+)\b.*?>.*?@si', '', $text); + } + else { + return preg_replace('@<('. implode('|', $tags) .')\b.*?>.*?@si', '', $text); + } + } + elseif($invert == FALSE) { + return preg_replace('@<(\w+)\b.*?>.*?@si', '', $text); + } + return $text; +} + +/////////////////////////////////////////////////////////////////// + +function formatText($text) +{ + $text = preg_replace('/http:\/\/([^\s]*)/i', + "http://$1", $text); + + $text = preg_replace('/https:\/\/([^\s]*)/i', + "https://$1", $text); + + $text = preg_replace('/\#([a-zA-Z0-9_Ç-Ã�]*)/', + "#$1", $text); + + $text = preg_replace('/\@([a-zA-Z0-9_Ç-Ã�]*)/', + "@$1 ", $text); + + $text = force_wordbreak($text, 80); + return $text; +} + + + diff --git a/web/yaamp/core/core.php b/web/yaamp/core/core.php new file mode 100644 index 0000000..c538510 --- /dev/null +++ b/web/yaamp/core/core.php @@ -0,0 +1,11 @@ +api_key = $api_key; +// } + + protected function jsonQuery($url) { + $opts = array('http' => + array( + 'method' => 'GET', + 'timeout' => 10 + ) + ); + +// debuglog($url); + $context = stream_context_create($opts); + $feed = @file_get_contents($url, false, $context); +// debuglog($feed); + + if(!$feed) + { + debuglog("c-cex error $url"); + return null; //array('error' => 'Invalid parameters'); + } + else + { + $a = json_decode($feed, true); + if(!$a) debuglog($feed); + + return $a; + } + } + + + public function getTickerInfo($pair){ + $json = $this->jsonQuery($this->api_url.$pair.'.json'); + return $json['ticker']; + } + + + public function getPairs(){ + $json = $this->jsonQuery($this->api_url.'pairs.json'); + return isset($json['pairs'])? $json['pairs']: array(); + } + + + public function getVolumes($hours=24,$pair=false){ + $url = ($pair) ? 'volume' : 'lastvolumes&pair='.$pair.'&'; + return $this->jsonQuery($this->api_url."s.html?a=".$url."&h=".$hours); + } + + public function getOrders($pair,$self = 0){ + $self = intval( (bool)$self );//return only 0 or 1 + return $this->jsonQuery($this->api_url."r.html?key={$this->api_key}&a=orderlist&self={$self}&pair={$pair}"); + } + + public function getHistory($pair,$fromTime = false,$toTime = false,$self = false){ + + if($fromTime === false){ + $fromTime = 0; + } + + if($toTime === false){ + $toTime = time(); + } + + $fromDate = date('Y-d-m',(int)$fromTime); + $toDate = date('Y-d-m',(int)$toTime); + + $url = ($self) ? "r.html?key={$this->api_key}&" : "s.html?"; + return $this->jsonQuery($this->api_url.$url."a=tradehistory&d1={$fromDate}&d2={$toDate}&pair={$pair}"); + } + + public function makeOrder($type,$pair,$quantity,$price){ + if(strtolower($type) == 'sell'){ + $type = 's'; + } + if(strtolower($type) == 'buy'){ + $type = 'b'; + } + return $this->jsonQuery($this->api_url."r.html?key={$this->api_key}&a=makeorder&pair={$pair}&q={$quantity}&t={$type}&r={$price}"); + } + + public function cancelOrder($order){ + return $this->jsonQuery($this->api_url."r.html?key={$this->api_key}&a=cancelorder&id={$order}"); + } + + public function getBalance(){ + return $this->jsonQuery($this->api_url."r.html?key={$this->api_key}&a=getbalance"); + } + +} + diff --git a/web/yaamp/core/exchange/cryptsy.php b/web/yaamp/core/exchange/cryptsy.php new file mode 100755 index 0000000..de72cce --- /dev/null +++ b/web/yaamp/core/exchange/cryptsy.php @@ -0,0 +1,63 @@ +api_key = $api_key; +// $this->api_secret = $api_secret; +// } + + private function query(array $req = array()) { + // API settings + $key = $this->api_key; + $secret = $this->api_secret; + + // generate a nonce to avoid problems with 32bit systems + $mt = explode(' ', microtime()); + $req['nonce'] = $mt[1].substr($mt[0], 2, 6); + + // generate the POST data string + $post_data = http_build_query($req, '', '&'); + $sign = hash_hmac('sha512', $post_data, $secret); + + // generate the extra headers + $headers = array( + 'Key: '.$key, + 'Sign: '.$sign, + ); + + // curl handle (initialize if required) + static $ch = null; + if (is_null($ch)) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, + 'Mozilla/4.0 (compatible; Poloniex PHP bot; '.php_uname('a').'; PHP/'.phpversion().')' + ); + } + curl_setopt($ch, CURLOPT_URL, $this->trading_url); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + + // run the query + $res = curl_exec($ch); + + if ($res === false) throw new Exception('Curl error: '.curl_error($ch)); + //echo $res; + $dec = json_decode($res, true); + if (!$dec){ + //throw new Exception('Invalid data: '.$res); + return false; + }else{ + return $dec; + } + } + + protected function retrieveJSON($URL) { + $opts = array('http' => + array( + 'method' => 'GET', + 'timeout' => 20 + ) + ); + $context = stream_context_create($opts); + $feed = @file_get_contents($URL, false, $context); + if(!$feed) return null; + + $json = json_decode($feed, true); + return $json; + } + + public function get_balances() { + return $this->query( + array( + 'command' => 'returnBalances' + ) + ); + } + + public function get_deposit_addresses() { + return $this->query( + array( + 'command' => 'returnDepositAddresses' + ) + ); + } + + public function generate_address($currency) { + debuglog("generate_address($currency)"); + return $this->query( + array( + 'command' => 'generateNewAddress', + 'currency' => strtoupper($currency), + ) + ); + } + + public function get_open_orders($pair) { + return $this->query( + array( + 'command' => 'returnOpenOrders', + 'currencyPair' => strtoupper($pair) + ) + ); + } + + public function get_my_trade_history($pair) { + return $this->query( + array( + 'command' => 'returnTradeHistory', + 'currencyPair' => strtoupper($pair) + ) + ); + } + + public function buy($pair, $rate, $amount) { + return $this->query( + array( + 'command' => 'buy', + 'currencyPair' => strtoupper($pair), + 'rate' => $rate, + 'amount' => $amount + ) + ); + } + + public function sell($pair, $rate, $amount) { + return $this->query( + array( + 'command' => 'sell', + 'currencyPair' => strtoupper($pair), + 'rate' => $rate, + 'amount' => $amount + ) + ); + } + + public function cancel_order($pair, $order_number) { + return $this->query( + array( + 'command' => 'cancelOrder', + 'currencyPair' => strtoupper($pair), + 'orderNumber' => $order_number + ) + ); + } + + public function withdraw($currency, $amount, $address) { + return $this->query( + array( + 'command' => 'withdraw', + 'currency' => strtoupper($currency), + 'amount' => $amount, + 'address' => $address + ) + ); + } + + public function get_trade_history($pair) { + $trades = $this->retrieveJSON($this->public_url.'?command=returnTradeHistory¤cyPair='.strtoupper($pair)); + return $trades; + } + + public function get_order_book($pair) { + $orders = $this->retrieveJSON($this->public_url.'?command=returnOrderBook¤cyPair='.strtoupper($pair)); + return $orders; + } + + public function get_volume() { + $volume = $this->retrieveJSON($this->public_url.'?command=return24hVolume'); + return $volume; + } + + public function get_ticker($pair = "ALL") { + $pair = strtoupper($pair); + $prices = $this->retrieveJSON($this->public_url.'?command=returnTicker'); + if($pair == "ALL"){ + return $prices; + }else{ + $pair = strtoupper($pair); + if(isset($prices[$pair])){ + return $prices[$pair]; + }else{ + return array(); + } + } + } + + public function get_trading_pairs() { + $tickers = $this->retrieveJSON($this->public_url.'?command=returnTicker'); + return array_keys($tickers); + } + + public function get_currencies() { + $tickers = $this->retrieveJSON($this->public_url.'?command=returnCurrencies'); + return $tickers; + } + + public function get_total_btc_balance() { + $balances = $this->get_balances(); + $prices = $this->get_ticker(); + + $tot_btc = 0; + + foreach($balances as $coin => $amount){ + $pair = "BTC_".strtoupper($coin); + + // convert coin balances to btc value + if($amount > 0){ + if($coin != "BTC"){ + $tot_btc += $amount * $prices[$pair]; + }else{ + $tot_btc += $amount; + } + } + + // process open orders as well + if($coin != "BTC"){ + $open_orders = $this->get_open_orders($pair); + foreach($open_orders as $order){ + if($order['type'] == 'buy'){ + $tot_btc += $order['total']; + }elseif($order['type'] == 'sell'){ + $tot_btc += $order['amount'] * $prices[$pair]; + } + } + } + } + + return $tot_btc; + } + } + + diff --git a/web/yaamp/core/exchange/yobit.php b/web/yaamp/core/exchange/yobit.php new file mode 100755 index 0000000..4bc804b --- /dev/null +++ b/web/yaamp/core/exchange/yobit.php @@ -0,0 +1,77 @@ +marketname = $marketname; + } + + public function doTrading() + { + $this->orders = $this->loadOrders(); + foreach($this->orders as $order) + { + $cexcoin = new CExchangeCoin($order->coin, $this->marketname); + // cancel if too high + // add to our db if not there already + } + + $list = getdbolist('db_orders', "market='$this->marketname'"); + foreach($list as $db_order) + { + $found = false; + foreach($this->orders as $order) + { + if($order->orderid == $db_order->uuid) + { + $found = true; + break; + } + } + + if(!$found) + { + debuglog("$this->marketname deleting order"); + $db_order->delete(); + } + } + + $savebalance = getdbosql('db_balances', "name='cryptsy'"); + $savebalance->balance = 0; + + $this->balances = $this->loadBalances(); + foreach($this->balances as $balance) + { + if($balance->amount <= 0) continue; + $cexcoin = new CExchangeCoin($balance->coin, $this->marketname); + + foreach($cexcoin->bids as $bid) + { + if($balance->amount*1.5 < $bid->amount && !$coin->sellonbid) break; + + $sellamount = min($balance->amount, $bid->price); + if($sellamount*$bid->price < $this->get_mintrade()) continue; + + $cex->sell($sellamount, $bid->price); + $balance->amount -= $sellamount; + + sleep(1); + } + + $cexcoin = new CExchangeCoin($balance->coin, $this->marketname); + if($balance->amount*$cexcoin->ask < $this->get_mintrade()) continue; + + $cex->sell($balance->amount, $cexcoin->ask); + sleep(1); + } + + if($this->balance_btc >= $this->get_minwithdraw()) + { + debuglog("withdraw $this->marketname $this->balance_btc"); + $this->withdraw($this->balance_btc); + } + } + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class CExchangeCryptsy extends CExchange +{ + protected function loadOrders() + { + $ordertab = array(); + + $orders = cryptsy_api_query('allmyorders'); + if(!$orders) return $ordertab; + if(!isset($orders['return'])) return $ordertab; + + foreach($orders['return'] as $order) + { + if(!isset($order['marketid'])) continue; + if(!isset($order['orderid'])) continue; + + $object = new object(); + $object->orderid = $order['orderid']; + $object->price = $order['price']; + $object->amount = $order['quantity']; + $object->marketid = $order['marketid']; + + $market = getdbosql('db_markets', "marketid=$object->marketid"); + if(!$market) continue; + + $object->coin = getdbo('db_coins', $market->coinid); + if(!$object->coin) continue; + + $ordertab[] = $object; + } + + return $ordertab; + } + + protected function loadBalances() + { + $balancetab = array(); + + $balances = cryptsy_api_query('getinfo'); + if(!$balances) return; + if(!isset($balances['return'])) + { + debuglog($balances); + return $balancetab; + } + + foreach($balances['return']['balances_available'] as $symbol=>$balance) + { + $balance = floatval($balance); + + if($symbol == 'Points') continue; + if($symbol == 'BTC') + { + $this->balance_btc = floatval($balance); + continue; + } + + if($symbol == 'LTC') + { + $this->balance_ltc = floatval($balance); + continue; + } + + if(!$balance) continue; + + $object = new object(); + $object->balance = $balance; + + $object->coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$object->coin) continue; + + $balancetab[] = $object; + } + + return $balancetab; + } + + protected function withdraw($amount) + { + $res = cryptsy_api_query('makewithdrawal', array('address'=>'14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9', 'amount'=>$amount)); + debuglog($res); + + if($res && $res['success']) + { + $withdraw = new db_withdraws; + $withdraw->market = 'cryptsy'; + $withdraw->address = '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'; + $withdraw->amount = $amount; + $withdraw->time = time(); + $withdraw->save(); + } + } + +}; + + + + diff --git a/web/yaamp/core/exchange2/CExchangeCoin.php b/web/yaamp/core/exchange2/CExchangeCoin.php new file mode 100644 index 0000000..71c8ed3 --- /dev/null +++ b/web/yaamp/core/exchange2/CExchangeCoin.php @@ -0,0 +1,84 @@ +coin = $coin; + $this->marketname = $marketname; + + $this->market = getdbosql('db_markets', "coinid=$coin->id and name='$marketname'"); + if(!$this->market) return; + } + + public static function create($coin, $marketname) + { + switch($marketname) + { + case 'cryptsy': + $cexcoin = new CExchangeCoinCryptsy($coin, $marketname); + break; + } + + $cexcoin->load(); + return $cexcoin; + } + +}; + +class CExchangeCoinCryptsy extends CExchangeCoin +{ + private $marketid; + + protected function load() + { + $ticker = getCryptsyTicker($market->marketid); + if(!$ticker || !isset($ticker->return->$symbol->sellorders[0])) continue; + + /// + } + + protected function sell($amount, $price) + { + $res = cryptsy_api_query('createorder', + array('marketid'=>$this->marketid, 'ordertype'=>'Sell', 'quantity'=>$amount, 'price'=>$price)); + if(!$res || !isset($res['orderid'])) return; + + $db_order = new db_orders; + $db_order->market = 'cryptsy'; + $db_order->coinid = $this->coin->id; + $db_order->amount = $balance; + $db_order->price = $sellprice; + $db_order->ask = $price; + $db_order->bid = $price; + $db_order->uuid = $res['orderid']; + $db_order->created = time(); + $db_order->save(); + } + + protected function cancel($amount, $price) + { + } + +}; + + + + diff --git a/web/yaamp/core/exchange2/exchange.php b/web/yaamp/core/exchange2/exchange.php new file mode 100644 index 0000000..23cb64b --- /dev/null +++ b/web/yaamp/core/exchange2/exchange.php @@ -0,0 +1,7 @@ +$value){ + $r[] = "$key=" . rawurlencode($value); + } + return $method."&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $r)); +} + +function buildAuthorizationHeader($oauth) +{ + $r = 'Authorization: OAuth '; + $values = array(); + foreach($oauth as $key=>$value) + $values[] = "$key=\"" . rawurlencode($value) . "\""; + $r .= implode(', ', $values); + return $r; +} + +// function docurloauth($url, $oauth_access_token, $oauth_access_token_secret) +// { +// $consumer_key = CONSUMER_KEY; +// $consumer_secret = CONSUMER_SECRET; + +// $oauth = array( 'oauth_consumer_key' => $consumer_key, +// 'oauth_nonce' => time(), +// 'oauth_signature_method' => 'HMAC-SHA1', +// 'oauth_token' => $oauth_access_token, +// 'oauth_timestamp' => time(), +// 'oauth_version' => '1.0'); + +// $base_info = buildBaseString($url, 'POST', $oauth); +// $composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret); +// $oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true)); +// $oauth['oauth_signature'] = $oauth_signature; + +// $header = array(buildAuthorizationHeader($oauth), 'Expect:'); +// $options = array( CURLOPT_HTTPHEADER => $header, +// CURLOPT_HEADER => false, +// CURLOPT_URL => $url, +// CURLOPT_RETURNTRANSFER => true, +// CURLOPT_SSL_VERIFYPEER => false); + +// return $options; +// } + + diff --git a/web/yaamp/core/functions/easybitcoin.php b/web/yaamp/core/functions/easybitcoin.php new file mode 100644 index 0000000..1a22ed1 --- /dev/null +++ b/web/yaamp/core/functions/easybitcoin.php @@ -0,0 +1,220 @@ +setSSL('/full/path/to/mycertificate.cert'); + +// Make calls to bitcoind as methods for your object. Responses are returned as an array. +// Examples: +$bitcoin->getinfo(); +$bitcoin->getrawtransaction('0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098',1); +$bitcoin->getblock('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'); + +// The full response (not usually needed) is stored in $this->response while the raw JSON is stored in $this->raw_response + +// When a call fails for any reason, it will return FALSE and put the error message in $this->error +// Example: +echo $bitcoin->error; + +// The HTTP status code can be found in $this->status and will either be a valid HTTP status code or will be 0 if cURL was unable to connect. +// Example: +echo $bitcoin->status; + +*/ + +class Bitcoin { + // Configuration options + private $username; + private $password; + private $proto; + private $host; + private $port; + private $url; + private $CACertificate; + + // Information and debugging + public $status; + public $error; + public $raw_response; + public $response; + + private $id = 0; + + /** + * @param string $username + * @param string $password + * @param string $host + * @param int $port + * @param string $proto + * @param string $url + */ + function __construct($username, $password, $host = 'localhost', $port = 8332, $url = null) { + $this->username = $username; + $this->password = $password; + $this->host = $host; + $this->port = $port; + $this->url = $url; + + // Set some defaults + $this->proto = 'http'; + $this->CACertificate = null; + } + + /** + * @param string|null $certificate + */ + function setSSL($certificate = null) { + $this->proto = 'https'; // force HTTPS + $this->CACertificate = $certificate; + } + + function __call($method, $params) + { + $this->status = null; + $this->error = null; + $this->raw_response = null; + $this->response = null; + + // The ID should be unique for each call + $this->id++; + + // If no parameters are passed, this will be an empty array + if($method == 'getblocktemplate') + { + $param = $params[0]; + $request = "{\"method\":\"$method\",\"params\":[$param],\"id\":$this->id}"; + // debuglog($request); + } + + else + { + $params = array_values($params); + + // Build the request, it's ok that params might have any empty array + $request = json_encode(array( + 'method' => $method, + 'params' => $params, + 'id' => $this->id + )); + } + + // Build the cURL session + $curl = curl_init("{$this->proto}://{$this->username}:{$this->password}@{$this->host}:{$this->port}/{$this->url}"); + $options = array( + CURLOPT_CONNECTTIMEOUT => 20, + CURLOPT_TIMEOUT => 30, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_FOLLOWLOCATION => TRUE, + CURLOPT_MAXREDIRS => 10, + CURLOPT_HTTPHEADER => array('Content-type: application/json'), + CURLOPT_POST => TRUE, + CURLOPT_POSTFIELDS => $request + ); + + if ($this->proto == 'https') { + // If the CA Certificate was specified we change CURL to look for it + if ($this->CACertificate != null) { + $options[CURLOPT_CAINFO] = $this->CACertificate; + $options[CURLOPT_CAPATH] = DIRNAME($this->CACertificate); + } + else { + // If not we need to assume the SSL cannot be verified so we set this flag to FALSE to allow the connection + $options[CURLOPT_SSL_VERIFYPEER] = FALSE; + } + } + + curl_setopt_array($curl, $options); + + // Execute the request and decode to an array + $this->raw_response = curl_exec($curl); +// debuglog($this->raw_response); + $this->response = json_decode($this->raw_response, TRUE); + + // If the status is not 200, something is wrong + $this->status = curl_getinfo($curl, CURLINFO_HTTP_CODE); + + // If there was no error, this will be an empty string + $curl_error = curl_error($curl); + + curl_close($curl); +// debuglog($this->response); + + if (!empty($curl_error)) { + $this->error = $curl_error; + } + + if (isset($this->response['error']) && $this->response['error']) { + // If bitcoind returned an error, put that in $this->error + $this->error = strtolower($this->response['error']['message']); + } + + elseif ($this->status != 200) { + // If bitcoind didn't return a nice error message, we need to make our own + switch ($this->status) { + case 400: + $this->error = 'HTTP_BAD_REQUEST'; + break; + case 401: + $this->error = 'HTTP_UNAUTHORIZED'; + break; + case 403: + $this->error = 'HTTP_FORBIDDEN'; + break; + case 404: + $this->error = 'HTTP_NOT_FOUND'; + break; + } + } + + if ($this->error) { + return FALSE; + } + + return $this->response['result']; + } +} + + + + diff --git a/web/yaamp/core/functions/functions.php b/web/yaamp/core/functions/functions.php new file mode 100644 index 0000000..cd8ccf0 --- /dev/null +++ b/web/yaamp/core/functions/functions.php @@ -0,0 +1,11 @@ +memcache = memcache_connect("127.0.0.1", 11211); + } + + public function get($key) + { + return memcache_get($this->memcache, $key); + } + + public function set($key, $value, $t=30) + { + memcache_set($this->memcache, $key, $value, 0, $t); + } + + //////////////////////////////////////////////////////////////// + + public function get_database_count($key, $query, $params=array()) + { + + } + + public function get_database_scalar($key, $query, $params=array()) + { + $value = $this->get($key); + if($value === false) + { + $value = dboscalar($query, $params); + $this->set($key, $value); + } + + return $value; + } + + public function get_database_count_ex($key, $table, $query, $params=array()) + { + $value = $this->get($key); + if($value === false) + { + $value = getdbocount($table, $query, $params); + $this->set($key, $value); + } + + return $value; + } + + public function get_database_row($key, $query, $params=array()) + { + $value = $this->get($key); + if($value === false) + { + $value = dborow($query, $params); + $this->set($key, $value); + } + + return $value; + } + + public function add_monitoring_function($name, $d1) + { + return; + $count = memcache_get($this->memcache, "$name-count"); + memcache_set($this->memcache, "$name-count", $count+1); + + $d = memcache_get($this->memcache, "$name-time"); + memcache_set($this->memcache, "$name-time", $d+$d1); + + $a = memcache_get($this->memcache, 'url-map'); + if(!$a) $a = array(); + + $a[$name] = $count+1; + memcache_set($this->memcache, 'url-map', $a); + } +}; + + + + diff --git a/web/yaamp/core/functions/url.php b/web/yaamp/core/functions/url.php new file mode 100644 index 0000000..ce070f9 --- /dev/null +++ b/web/yaamp/core/functions/url.php @@ -0,0 +1,79 @@ +getAttribute($keyname) != $fieldname) return $attribute; + + $value = $meta->getAttribute('value'); + if(empty($value)) + { + $value = $meta->getAttribute('content'); + if(empty($value)) + { + // debuglog("* found $keyname, $fieldname, but failed to get value"); + return $attribute; + } + } + + $attribute = !$maxsize? $value: substr($value, 0, $maxsize); + return $attribute; +} + +function processUrl($url) +{ + libxml_use_internal_errors(true); + + $html = @file_get_contents_curl($url); + if(!$html) + { + debuglog("* no response from $url"); + return null; + } + + $encoding = mb_detect_encoding($html, 'UTF-8, ISO-8859-1', true); + if($encoding == 'UTF-8') + $html = mb_convert_encoding($html, 'HTML-ENTITIES', $encoding); + + $doc = new DOMDocument(); + $res = $doc->loadHTML($html); + + $info = array(); + + $nodes = $doc->getElementsByTagName('title'); + if(!$nodes) return null; + + $info['title'] = substr($nodes->item(0)->nodeValue, 0, 256); + + $metas = $doc->getElementsByTagName('meta'); + for($i = 0; $i < $metas->length; $i++) + { + $meta = $metas->item($i); + + $info['overview'] = processUrlKeyword($meta, 'name', 'description', $info['overview'], 512, false); + $info['overview'] = processUrlKeyword($meta, 'property', 'og:description', $info['overview'], 512); + $info['overview'] = processUrlKeyword($meta, 'property', 'rnews:description', $info['overview'], 512); + + $info['image_url'] = processUrlKeyword($meta, 'name', 'image', $info['image_url'], 1024, false); + $info['image_url'] = processUrlKeyword($meta, 'property', 'og:image', $info['image_url'], 1024); + + $info['site_name'] = processUrlKeyword($meta, 'name', 'site_name', $info['site_name'], 64, false); + $info['site_name'] = processUrlKeyword($meta, 'property', 'og:site_name', $info['site_name'], 64); + $info['site_name'] = processUrlKeyword($meta, 'name', 'application-name', $info['site_name'], 64, false); + + $info['type'] = processUrlKeyword($meta, 'name', 'type', $info['type'], 64, false); + $info['type'] = processUrlKeyword($meta, 'property', 'og:type', $info['type'], 64); + + $info['image_url'] = processUrlKeyword($meta, 'name', 'twitter:image', $info['image_url'], 1024); + $info['player'] = processUrlKeyword($meta, 'name', 'twitter:player', $info['player'], 1024); + $info['player_width'] = processUrlKeyword($meta, 'name', 'twitter:player:width', $info['player_width']); + $info['player_height'] = processUrlKeyword($meta, 'name', 'twitter:player:height', $info['player_height']); + } + + return $info; +} + + + + diff --git a/web/yaamp/core/functions/yaamp.php b/web/yaamp/core/functions/yaamp.php new file mode 100755 index 0000000..762da86 --- /dev/null +++ b/web/yaamp/core/functions/yaamp.php @@ -0,0 +1,365 @@ + 1, + 'scrypt' => 1, + 'scryptn' => 0.5, + 'x11' => 5.5, + 'x13' => 3.9, + 'x14' => 3.7, + 'x15' => 3.5, + 'zr5' => 3, + 'nist5' => 16, + 'neoscrypt' => 0.3, + 'lyra2' => 1.3, + 'quark' => 5, + 'fresh' => 5, + 'qubit' => 5, + 'skein' => 90, + 'groestl' => 5, + 'blake' => 300, + 'keccak' => 160, + ); + + if(!isset($a[$algo])) + return 0; + + return $a[$algo]; +} + +function getAlgoColors($algo) +{ + $a = array( + 'sha256' => '#d0d0a0', + 'scrypt' => '#c0c0e0', + 'neoscrypt' => '#a0d0f0', + 'scryptn' => '#d0d0d0', + 'x11' => '#f0f0a0', + 'x13' => '#d0f0c0', + 'x14' => '#a0f0c0', + 'x15' => '#f0b0a0', + 'nist5' => '#f0d0f0', + 'quark' => '#c0c0c0', + 'qubit' => '#d0a0f0', + 'lyra2' => '#80a0f0', + ); + + if(!isset($a[$algo])) + return '#ffffff'; + + return $a[$algo]; +} + +function getAlgoPort($algo) +{ + $a = array( + 'sha256' => 3333, + 'scrypt' => 3433, + 'x11' => 3533, + 'x13' => 3633, + 'x15' => 3733, + 'nist5' => 3833, + 'x14' => 3933, + 'quark' => 4033, + 'fresh' => 4133, + 'neoscrypt' => 4233, + 'scryptn' => 4333, + 'lyra2' => 4433, + 'blake' => 4533, + 'jha' => 4633, + 'qubit' => 4733, + 'zr5' => 4833, + 'skein' => 4933, + 'groestl' => 5033, + 'keccak' => 5133, + ); + + if(!isset($a[$algo])) + return 3033; + + return $a[$algo]; +} + +//////////////////////////////////////////////////////////////////////// + +function yaamp_fee($algo) +{ + $fee = controller()->memcache->get("yaamp_fee-$algo"); + if($fee) return $fee; + + $norm = yaamp_get_algo_norm($algo); + if($norm == 0) $norm = 1; + + $hashrate = getdbosql('db_hashrate', "algo=:algo order by time desc", array(':algo'=>$algo)); + if(!$hashrate || !$hashrate->difficulty) return 1; + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_pool_rate_coinonly-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and time>$delay and algo=:algo and jobid=0", array(':algo'=>$algo)); + +// $fee = round(log($hashrate->hashrate * $norm / 1000000 / $hashrate->difficulty + 1), 1) + YAAMP_FEES_MINING; + $fee = round(log($rate * $norm / 2000000 / $hashrate->difficulty + 1), 1) + YAAMP_FEES_MINING; + + controller()->memcache->set("yaamp_fee-$algo", $fee); + return $fee; +} + +function take_yaamp_fee($v, $algo) +{ + return $v - ($v * yaamp_fee($algo) / 100); +} + +function yaamp_hashrate_constant($algo=null) +{ + return pow(2, 42); // 0x400 00000000 +} + +function yaamp_hashrate_step() +{ + return 300; +} + +function yaamp_profitability($coin) +{ + if(!$coin->difficulty) return 0; + + $btcmhd = 20116.56761169 / $coin->difficulty * $coin->reward * $coin->price; + if(!$coin->auxpow && $coin->rpcencoding == 'POW') + { + $listaux = getdbolist('db_coins', "enable and visible and auto_ready and auxpow and algo='$coin->algo'"); + foreach($listaux as $aux) + { + if(!$aux->difficulty) continue; + + $btcmhdaux = 20116.56761169 / $aux->difficulty * $aux->reward * $aux->price; + $btcmhd += $btcmhdaux; + } + } + + if($coin->algo == 'sha256') $btcmhd *= 1000; + return $btcmhd; +} + +function yaamp_convert_amount_user($coin, $amount, $user) +{ + $refcoin = getdbo('db_coins', $user->coinid); + if(!$refcoin) $refcoin = getdbosql('db_coins', "symbol='BTC'"); + if(!$refcoin || $refcoin->price2<=0) return 0; + + $value = $amount * $coin->price2 / $refcoin->price2; + return $value; +} + +function yaamp_convert_earnings_user($user, $status) +{ + $refcoin = getdbo('db_coins', $user->coinid); + if(!$refcoin) $refcoin = getdbosql('db_coins', "symbol='BTC'"); + if(!$refcoin || $refcoin->price2<=0) return 0; + + $value = dboscalar("select sum(amount*price) from earnings where $status and userid=$user->id"); + $value = $value/$refcoin->price2; + + return $value; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +function yaamp_pool_rate($algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_pool_rate-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and time>$delay and algo=:algo", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_pool_rate_bad($algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_pool_rate_bad-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where not valid and time>$delay and algo=:algo", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_pool_rate_rentable($algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_pool_rate_rentable-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and extranonce1 and time>$delay and algo=:algo", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_user_rate($userid, $algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_user_rate-$userid-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and time>$delay and userid=$userid and algo=:algo", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_user_rate_bad($userid, $algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_user_rate_bad-$userid-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where not valid and time>$delay and userid=$userid and algo=:algo", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_worker_rate($workerid, $algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_worker_rate-$workerid-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and time>$delay and workerid=$workerid"); + + return $rate; +} + +function yaamp_worker_rate_bad($workerid, $algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_worker_rate_bad-$workerid-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where not valid and time>$delay and workerid=$workerid"); + + return empty($rate)? 0: $rate; +} + +function yaamp_coin_rate($coinid) +{ + $coin = getdbo('db_coins', $coinid); + if(!$coin || !$coin->enable) return 0; + + $target = yaamp_hashrate_constant($coin->algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_coin_rate-$coinid", + "select sum(difficulty) * $target / $interval / 1000 from shares where valid and time>$delay and coinid=$coinid"); + + return $rate; +} + +function yaamp_rented_rate($algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_rented_rate-$algo", + "select sum(difficulty) * $target / $interval / 1000 from shares where time>$delay and algo=:algo and jobid!=0 and valid", array(':algo'=>$algo)); + + return $rate; +} + +function yaamp_job_rate($jobid) +{ + $job = getdbo('db_jobs', $jobid); + if(!$job) return 0; + + $target = yaamp_hashrate_constant($job->algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_job_rate-$jobid", + "select sum(difficulty) * $target / $interval / 1000 from jobsubmits where valid and time>$delay and jobid=$jobid"); + return $rate; +} + +function yaamp_job_rate_bad($jobid) +{ + $job = getdbo('db_jobs', $jobid); + if(!$job) return 0; + + $target = yaamp_hashrate_constant($job->algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_job_rate_bad-$jobid", + "select sum(difficulty) * $target / $interval / 1000 from jobsubmits where not valid and time>$delay and jobid=$jobid"); + + return $rate; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +function yaamp_pool_rate_pow($algo=null) +{ + if(!$algo) $algo = user()->getState('yaamp-algo'); + + $target = yaamp_hashrate_constant($algo); + $interval = yaamp_hashrate_step(); + $delay = time()-$interval; + + $rate = controller()->memcache->get_database_scalar("yaamp_pool_rate_pow-$algo", + "select sum(shares.difficulty) * $target / $interval / 1000 from shares, coins + where shares.valid and shares.time>$delay and shares.algo=:algo and + shares.coinid=coins.id and coins.rpcencoding='POW'", array(':algo'=>$algo)); + + return $rate; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +function yaamp_renter_account($renter) +{ + if(YAAMP_PRODUCTION) + return "renter-prod-$renter->id"; + else + return "renter-dev-$renter->id"; +} + + + + + + diff --git a/web/yaamp/core/trading/bittrex_trading.php b/web/yaamp/core/trading/bittrex_trading.php new file mode 100644 index 0000000..df59cee --- /dev/null +++ b/web/yaamp/core/trading/bittrex_trading.php @@ -0,0 +1,221 @@ +success) return; + + foreach($orders->result as $order) + { + $symbol = substr($order->Exchange, 4); + $pair = $order->Exchange; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) continue; + if($coin->dontsell) continue; + + $ticker = bittrex_api_query('public/getticker', "&market=$order->Exchange"); + if(!$ticker || !$ticker->success || !$ticker->result) continue; + + $ask = bitcoinvaluetoa($ticker->result->Ask); + $sellprice = bitcoinvaluetoa($order->Limit); + + // flush orders not on the ask + if($ask+0.00000005 < $sellprice || $flushall) + { +// debuglog("bittrex cancel order $order->Exchange $sellprice -> $ask"); + bittrex_api_query('market/cancel', "&uuid=$order->OrderUuid"); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order->OrderUuid)); + if($db_order) $db_order->delete(); + + sleep(1); + } + + // add existing orders (shouldnt happen after init) + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order->OrderUuid)); + if($db_order) continue; + + debuglog("adding order $coin->symbol"); + + // $ticker = bittrex_api_query('public/getticker', "&market=$pair"); + // $sellprice = bitcoinvaluetoa($ticker->result->Ask); + + $db_order = new db_orders; + $db_order->market = 'bittrex'; + $db_order->coinid = $coin->id; + $db_order->amount = $order->Quantity; + $db_order->price = $sellprice; + $db_order->ask = $ticker->result->Ask; + $db_order->bid = $ticker->result->Bid; + $db_order->uuid = $order->OrderUuid; + $db_order->created = time(); + $db_order->save(); + } + } + + // flush obsolete orders + $list = getdbolist('db_orders', "market='bittrex'"); + foreach($list as $db_order) + { + $coin = getdbo('db_coins', $db_order->coinid); + if(!$coin) continue; + + $found = false; + foreach($orders->result as $order) + if($order->OrderUuid == $db_order->uuid) + { + $found = true; + break; + } + + if(!$found) + { + debuglog("bittrex deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + +// if($flushall) +// { +// debuglog("bittrex flushall"); +// return; +// } + + sleep(2); + + // add orders + $balances = bittrex_api_query('account/getbalances'); + if(!$balances || !isset($balances->result) || !$balances->success) return; + + $savebalance = getdbosql('db_balances', "name='bittrex'"); + $savebalance->balance = 0; + + foreach($balances->result as $balance) + { + if($balance->Currency == 'BTC') + { + $savebalance->balance = $balance->Available; + continue; + } + + $amount = floatval($balance->Available); + if(!$amount) continue; + + // debuglog($balance->Currency); + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$balance->Currency)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='bittrex'"); + if($market) + { + $market->lasttraded = time(); + $market->save(); + } + + if($amount*$coin->price < 0.00050000) continue; + $pair = "BTC-$balance->Currency"; + + $data = bittrex_api_query('public/getorderbook', "&market=$pair&type=buy&depth=10"); + if(!$data || !$data->success) continue; + + for($i = 0; $i < 5 && $amount >= 0; $i++) + { + if(!isset($data->result->buy[$i])) break; + + $nextbuy = $data->result->buy[$i]; + if($amount*1.1 < $nextbuy->Quantity) break; + + $sellprice = bitcoinvaluetoa($nextbuy->Rate); + $sellamount = min($amount, $nextbuy->Quantity); + + if($sellamount*$sellprice < 0.00050000) continue; + + debuglog("bittrex selling market $pair, $sellamount, $sellprice"); + $res = bittrex_api_query('market/selllimit', "&market=$pair&quantity=$sellamount&rate=$sellprice"); + + if(!$res->success) + { + debuglog($res); + break; + } + + $amount -= $sellamount; + } + + if($amount <= 0) continue; + + $ticker = bittrex_api_query('public/getticker', "&market=$pair"); + if(!$ticker || !$ticker->success || !$ticker->result) continue; + + if($coin->sellonbid) + $sellprice = bitcoinvaluetoa($ticker->result->Bid); + else + $sellprice = bitcoinvaluetoa($ticker->result->Ask); + if($amount*$sellprice < 0.00050000) continue; + +// debuglog("bittrex selling $pair, $amount, $sellprice"); + + $res = bittrex_api_query('market/selllimit', "&market=$pair&quantity=$amount&rate=$sellprice"); + if(!$res || !$res->success) + { + debuglog($res); + continue; + } + + $db_order = new db_orders; + $db_order->market = 'bittrex'; + $db_order->coinid = $coin->id; + $db_order->amount = $amount; + $db_order->price = $sellprice; + $db_order->ask = $ticker->result->Ask; + $db_order->bid = $ticker->result->Bid; + $db_order->uuid = $res->result->uuid; + $db_order->created = time(); + $db_order->save(); + + sleep(1); + } + + if($savebalance->balance >= 0.3) + { + $amount = $savebalance->balance; // - 0.0002; + debuglog("bittrex withdraw $amount to 14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + + sleep(1); + + $res = bittrex_api_query('account/withdraw', "¤cy=BTC&quantity=$amount&address=14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + debuglog($res); + + if($res && $res->success) + { + $withdraw = new db_withdraws; + $withdraw->market = 'bittrex'; + $withdraw->address = '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'; + $withdraw->amount = $amount; + $withdraw->time = time(); + $withdraw->uuid = $res->result->uuid; + $withdraw->save(); + + // $savebalance->balance = 0; + } + } + + $savebalance->save(); + + // debuglog('-------------- doBittrexTrading() done'); +} + + + + + + diff --git a/web/yaamp/core/trading/bleutrade_trading.php b/web/yaamp/core/trading/bleutrade_trading.php new file mode 100644 index 0000000..3cb10af --- /dev/null +++ b/web/yaamp/core/trading/bleutrade_trading.php @@ -0,0 +1,218 @@ +result as $order) + { + $e = explode('_', $order->Exchange); + $symbol = $e[0]; /// "Exchange" : "LTC_BTC", + $pair = $order->Exchange; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) continue; + if($coin->dontsell) continue; + + $ticker = bleutrade_api_query('public/getticker', "&market=$pair"); + if(!$ticker || !$ticker->success || !isset($ticker->result[0])) continue; + + $ask = bitcoinvaluetoa($ticker->result[0]->Ask); + $sellprice = bitcoinvaluetoa($order->Price); + + // flush orders not on the ask + if($ask+0.00000005 < $sellprice || $flushall) + { + // debuglog("bleutrade cancel order $order->Exchange $sellprice -> $ask"); + bleutrade_api_query('market/cancel', "&orderid=$order->OrderId"); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order->OrderId)); + if($db_order) $db_order->delete(); + + sleep(1); + } + + // add existing orders (shouldnt happen after init) + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order->OrderId)); + if($db_order) continue; + + debuglog("bleutrade adding order $coin->symbol"); + + // $ticker = bleutrade_api_query('public/getticker', "&market=$pair"); + // $sellprice = bitcoinvaluetoa($ticker->result->Ask); + + $db_order = new db_orders; + $db_order->market = 'bleutrade'; + $db_order->coinid = $coin->id; + $db_order->amount = $order->Quantity; + $db_order->price = $sellprice; + $db_order->ask = $ticker->result[0]->Ask; + $db_order->bid = $ticker->result[0]->Bid; + $db_order->uuid = $order->OrderId; + $db_order->created = time(); + $db_order->save(); + } + } + + // flush obsolete orders + $list = getdbolist('db_orders', "market='bleutrade'"); + foreach($list as $db_order) + { + $coin = getdbo('db_coins', $db_order->coinid); + if(!$coin) continue; + + $found = false; + foreach($orders->result as $order) + if($order->OrderId == $db_order->uuid) + { + $found = true; + break; + } + + if(!$found) + { + debuglog("bleutrade deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + +// if($flushall) +// { +// debuglog("bleutrade flushall got here"); +// return; +// } + + sleep(2); + + // add orders + $balances = bleutrade_api_query('account/getbalances'); +// debuglog($balances); + if(!$balances || !isset($balances->result) || !$balances->success) return; + + $savebalance = getdbosql('db_balances', "name='bleutrade'"); + $savebalance->balance = 0; + + foreach($balances->result as $balance) + { + if($balance->Currency == 'BTC') + { + $savebalance->balance = $balance->Available; + continue; + } + + $amount = floatval($balance->Available); + if(!$amount) continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$balance->Currency)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='bleutrade'"); + if($market) + { + $market->lasttraded = time(); + $market->save(); + } + + if($amount*$coin->price < 0.00001000) continue; + $pair = "{$balance->Currency}_BTC"; + + $data = bleutrade_api_query('public/getorderbook', "&market=$pair&type=BUY&depth=10"); + if(!$data) continue; + // if(!isset($data->result[0])) continue; + + for($i = 0; $i < 5; $i++) + { + if(!isset($data->result->buy[$i])) break; + + $nextbuy = $data->result->buy[$i]; + if($amount < $nextbuy->Quantity || $nextbuy->Quantity*$nextbuy->Rate < 0.00001000) + break; + + $sellprice = bitcoinvaluetoa($nextbuy->Rate); + +// debuglog("bleutrade selling market $pair, $nextbuy->Quantity, $sellprice"); + $res = bleutrade_api_query('market/selllimit', "&market=$pair&quantity=$nextbuy->Quantity&rate=$sellprice"); + + if(!$res->success) + { + debuglog($res); + break; + } + + $amount -= $nextbuy->Quantity; + } + + $ticker = bleutrade_api_query('public/getticker', "&market=$pair"); + if(!$ticker || !$ticker->success || !isset($ticker->result[0])) continue; + + if($coin->sellonbid) + $sellprice = bitcoinvaluetoa($ticker->result[0]->Bid); + else + $sellprice = bitcoinvaluetoa($ticker->result[0]->Ask); + if($amount*$sellprice < 0.00050000) continue; + + debuglog("bleutrade selling $pair, $amount, $sellprice"); + + $res = bleutrade_api_query('market/selllimit', "&market=$pair&quantity=$amount&rate=$sellprice"); + if(!$res || !$res->success || !isset($res->result)) + { + debuglog($res); + continue; + } + + $db_order = new db_orders; + $db_order->market = 'bleutrade'; + $db_order->coinid = $coin->id; + $db_order->amount = $amount; + $db_order->price = $sellprice; + $db_order->ask = $ticker->result[0]->Ask; + $db_order->bid = $ticker->result[0]->Bid; + $db_order->uuid = $res->result->orderid; + $db_order->created = time(); + $db_order->save(); + + sleep(1); + } + + if($savebalance->balance >= 0.2) + { + $amount = $savebalance->balance; // - 0.0002; + debuglog("bleutrade withdraw $amount to 14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + + sleep(1); + + $res = bleutrade_api_query('account/withdraw', "¤cy=BTC&quantity=$amount&address=14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + debuglog($res); + + if($res && $res->success) + { + $withdraw = new db_withdraws; + $withdraw->market = 'bleutrade'; + $withdraw->address = '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'; + $withdraw->amount = $amount; + $withdraw->time = time(); + $withdraw->uuid = $res->result->orderid; + $withdraw->save(); + + // $savebalance->balance = 0; + } + } + + $savebalance->save(); + + // debuglog('-------------- dobleutradeTrading() done'); +} + + + + + + diff --git a/web/yaamp/core/trading/c-cex_trading.php b/web/yaamp/core/trading/c-cex_trading.php new file mode 100644 index 0000000..02c295c --- /dev/null +++ b/web/yaamp/core/trading/c-cex_trading.php @@ -0,0 +1,192 @@ +dontsell) continue; + + $market2 = getdbosql('db_markets', "coinid=$coin->id and (name='bittrex' or name='cryptsy')"); + if($market2) continue; + + $pair = strtolower($coin->symbol).'-btc'; + +// debuglog("c-cex list order for $pair"); + sleep(5); + + $orders = $ccex->getOrders($pair, 1); + if(!$orders || isset($orders['error'])) continue; + + foreach($orders['return'] as $uuid=>$order) + { + $ticker = $ccex->getTickerInfo($pair); + if(!$ticker) continue; + + if($order['price'] > $ticker['sell']+0.00000005 || $flushall) + { +// debuglog("c-cex cancel order for $pair $uuid"); + sleep(5); + + $ccex->cancelOrder($uuid); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$uuid)); + if($db_order) $db_order->delete(); + + sleep(1); + } + + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$uuid)); + if($db_order) continue; + + debuglog("c-cex adding order $coin->symbol"); + + $db_order = new db_orders; + $db_order->market = 'c-cex'; + $db_order->coinid = $coin->id; + $db_order->amount = $order['amount']; + $db_order->price = $order['price']; + $db_order->ask = $ticker['sell']; + $db_order->bid = $ticker['buy']; + $db_order->uuid = $uuid; + $db_order->created = time(); + $db_order->save(); + } + } + + $list = getdbolist('db_orders', "coinid=$coin->id and market='c-cex'"); + foreach($list as $db_order) + { + $found = false; + foreach($orders['return'] as $uuid=>$order) + { + if($uuid == $db_order->uuid) + { + $found = true; + break; + } + } + + if(!$found) + { + debuglog("c-cex deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + } + + sleep(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + $savebalance = getdbosql('db_balances', "name='c-cex'"); + $savebalance->balance = 0; + +// debuglog("c-cex getbalance"); + sleep(5); + + $balances = $ccex->getBalance(); + if(!$balances || !isset($balances['return'])) return; + + foreach($balances['return'] as $balance) foreach($balance as $symbol=>$amount) + { + if(!$amount) continue; + if($symbol == 'btc') + { + $savebalance->balance = $amount; + continue; + } + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='c-cex'"); + if($market) + { + $market->lasttraded = time(); + $market->save(); + } + + if($amount*$coin->price < 0.00001000) continue; + $pair = "$symbol-btc"; + + //////////////////////// + + $maxprice = 0; + $maxamount = 0; + +// debuglog("c-cex list order for $pair all"); + sleep(5); + + $orders = $ccex->getOrders($pair, 0); + foreach($orders['return'] as $order) + { + if($order['type'] == 'sell') continue; + if($order['price'] > $maxprice) + { + $maxprice = $order['price']; + $maxamount = $order['amount']; + } + } + + // debuglog("maxbuy for $pair $maxamount $maxprice"); + if($amount >= $maxamount && $maxamount*$maxprice > 0.00001000) + { + $sellprice = bitcoinvaluetoa($maxprice); + + debuglog("c-cex selling market $pair, $maxamount, $sellprice"); + sleep(5); + + $res = $ccex->makeOrder('sell', $pair, $maxamount, $sellprice); + if(!$res || !isset($res['return'])) + debuglog($res); + else + $amount -= $maxamount; + + sleep(1); + } + + /// + + $ticker = $ccex->getTickerInfo($pair); + if(!$ticker) continue; + + $sellprice = bitcoinvaluetoa($ticker['sell']); + +// debuglog("c-cex selling $pair, $amount, $sellprice"); + sleep(5); + + $res = $ccex->makeOrder('sell', $pair, $amount, $sellprice); + if(!$res || !isset($res['return'])) continue; + + $db_order = new db_orders; + $db_order->market = 'c-cex'; + $db_order->coinid = $coin->id; + $db_order->amount = $amount; + $db_order->price = $sellprice; + $db_order->ask = $ticker['sell']; + $db_order->bid = $ticker['buy']; + $db_order->uuid = $res['return']; + $db_order->created = time(); + $db_order->save(); + } + + $savebalance->save(); + +// debuglog('-------------- doCCexTrading() done'); +} + + + + + + diff --git a/web/yaamp/core/trading/cryptsy_trading.php b/web/yaamp/core/trading/cryptsy_trading.php new file mode 100644 index 0000000..8e6d563 --- /dev/null +++ b/web/yaamp/core/trading/cryptsy_trading.php @@ -0,0 +1,231 @@ +$order['marketid'])); + if(!$market) continue; + + $coin = getdbo('db_coins', $market->coinid); + if(!$coin) continue; + + $symbol = $coin->symbol; + + $ticker = getCryptsyTicker($market->marketid); + if(!$ticker || !isset($ticker->return->$symbol->sellorders[0])) continue; + + $ask = bitcoinvaluetoa($ticker->return->$symbol->sellorders[0]->price); + $sellprice = bitcoinvaluetoa($order['price']); + + // flush orders not on the ask + if($ask+0.00000005 < $sellprice || $flushall) + { +// debuglog("cryptsy cancel order for $coin->symbol ($ask != $sellprice)"); + cryptsy_api_query('cancelorder', array('orderid'=>$order['orderid'])); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order['orderid'])); + if($db_order) $db_order->delete(); + + sleep(1); + } + + // add existing orders (shouldnt happen after init) + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order['orderid'])); + if($db_order) continue; + + debuglog("adding order $coin->symbol"); + + $db_order = new db_orders; + $db_order->market = 'cryptsy'; + $db_order->coinid = $coin->id; + $db_order->amount = $order['quantity']; + $db_order->price = $sellprice; + $db_order->ask = $ticker->return->$symbol->sellorders[0]->price; + $db_order->bid = isset($ticker->return->$symbol->buyorders)? $ticker->return->$symbol->buyorders[0]->price: 0; + $db_order->uuid = $order['orderid']; + $db_order->created = time(); + $db_order->save(); + } + } + + $list = getdbolist('db_orders', "market='cryptsy'"); + foreach($list as $db_order) + { + $coin = getdbo('db_coins', $db_order->coinid); + + $found = false; + foreach($orders['return'] as $order) + { + if(!isset($order['orderid'])) continue; + + if($order['orderid'] == $db_order->uuid) + { + $found = true; + break; + } + } + + if(!$found) + { + debuglog("cryptsy deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + +// if($flushall) +// { +// debuglog("cryptsy flushall"); +// return; +// } + + sleep(2); + + // add orders + $savebalance = getdbosql('db_balances', "name='cryptsy'"); + $savebalance->balance = 0; + + $balances = cryptsy_api_query('getinfo'); + if(!$balances) return; + if(!isset($balances['return'])) + { + debuglog($balances); + return; + } + + foreach($balances['return']['balances_available'] as $symbol=>$balance) + { + if($symbol == 'Points') continue; + if($symbol == 'BTC') + { + $savebalance->balance = floatval($balance); + continue; + } + + $balance = floatval($balance); + if(!$balance) continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='cryptsy'"); + if(!$market) continue; + + $market->lasttraded = time(); + $market->save(); + + if($balance*$market->price < 0.00001000) continue; + + $ticker = getCryptsyTicker($market->marketid); + if(!$ticker || !isset($ticker->return->$symbol->buyorders[0])) continue; + + // for 0 to 4 + { + $nextbuy = $ticker->return->$symbol->buyorders[0]; + if($balance >= $nextbuy->quantity && $nextbuy->quantity*$nextbuy->price > 0.00001000) + { + $sellprice = bitcoinvaluetoa($nextbuy->price); + debuglog("cryptsy selling market $coin->symbol, $nextbuy->quantity, $sellprice"); + + $res = cryptsy_api_query('createorder', + array('marketid'=>$market->marketid, 'ordertype'=>'Sell', 'quantity'=>$nextbuy->quantity, 'price'=>$sellprice)); + if($res) $balance -= $nextbuy->quantity; + + // TradingClearExchangeCoin($coin, $nextbuy->quantity, $ticker->return->$symbol->buyorders[1]->price, 'cryptsy'); + sleep(1); + } + + if($coin->sellonbid && $balance*$nextbuy->price > 0.00001000) + { + $sellprice = bitcoinvaluetoa($nextbuy->price); + debuglog("cryptsy selling market $coin->symbol, $balance, $sellprice"); + + $res = cryptsy_api_query('createorder', + array('marketid'=>$market->marketid, 'ordertype'=>'Sell', 'quantity'=>$balance, 'price'=>$sellprice)); + + // TradingClearExchangeCoin($coin, $balance, $ticker->return->$symbol->buyorders[1]->price, 'cryptsy'); + sleep(1); + continue; + } + } + + if($coin->sellonbid) + $sellprice = $ticker->return->$symbol->buyorders[0]->price; + else + $sellprice = $ticker->return->$symbol->sellorders[0]->price; + // if($balance * $sellprice < 0.0001) continue; + +// debuglog("cryptsy selling $coin->symbol, $sellprice, $balance"); + $res = cryptsy_api_query('createorder', + array('marketid'=>$market->marketid, 'ordertype'=>'Sell', 'quantity'=>$balance, 'price'=>$sellprice)); + if(!$res || !isset($res['orderid'])) continue; + + $db_order = new db_orders; + $db_order->market = 'cryptsy'; + $db_order->coinid = $coin->id; + $db_order->amount = $balance; + $db_order->price = $sellprice; + $db_order->ask = $ticker->return->$symbol->sellorders[0]->price; + $db_order->bid = $ticker->return->$symbol->buyorders[0]->price; + $db_order->uuid = $res['orderid']; + $db_order->created = time(); + $db_order->save(); + } + + if($savebalance->balance >= 0.3) + { + $amount = $savebalance->balance; // - 0.001; + debuglog("cryptsy withdraw $amount to 14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + + sleep(1); + + $res = cryptsy_api_query('makewithdrawal', + array('address'=>'14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9', 'amount'=>$amount)); + + debuglog($res); + if($res && $res['success']) + { + $withdraw = new db_withdraws; + $withdraw->market = 'cryptsy'; + $withdraw->address = '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'; + $withdraw->amount = $amount; + $withdraw->time = time(); + // $withdraw->uuid = $res->result->uuid; + $withdraw->save(); + + // $savebalance->balance = 0; + } + } + + $savebalance->save(); + + // debuglog('-------------- doCryptsyTrading() done'); +} + + + + + + diff --git a/web/yaamp/core/trading/poloniex_trading.php b/web/yaamp/core/trading/poloniex_trading.php new file mode 100644 index 0000000..389c999 --- /dev/null +++ b/web/yaamp/core/trading/poloniex_trading.php @@ -0,0 +1,179 @@ +get_ticker(); + if(!$tickers) return; + + // upgrade orders + $coins = getdbolist('db_coins', "enable and id in (select distinct coinid from markets where name='poloniex')"); + foreach($coins as $coin) + { + if($coin->dontsell) continue; + $pair = "BTC_$coin->symbol"; + if(!isset($tickers[$pair])) continue; + + $orders = $poloniex->get_open_orders($pair); + if(!$orders || !isset($orders[0])) + { + dborun("delete from orders where coinid=$coin->id and market='poloniex'"); + continue; + } + + foreach($orders as $order) + { + if(!isset($order['orderNumber'])) + { + debuglog($order); + continue; + } + + if($order['rate'] > $tickers[$pair]['lowestAsk']+0.00000005 || $flushall) + { +// debuglog("poloniex cancel order for $pair {$order['orderNumber']}"); + $poloniex->cancel_order($pair, $order['orderNumber']); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order['orderNumber'])); + if($db_order) $db_order->delete(); + + sleep(1); + } + + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$order['orderNumber'])); + if($db_order) continue; + + debuglog("poloniex adding order $coin->symbol"); + + $db_order = new db_orders; + $db_order->market = 'poloniex'; + $db_order->coinid = $coin->id; + $db_order->amount = $order['amount']; + $db_order->price = $order['rate']; + $db_order->ask = $tickers[$pair]['lowestAsk']; + $db_order->bid = $tickers[$pair]['highestBid']; + $db_order->uuid = $order['orderNumber']; + $db_order->created = time(); + $db_order->save(); + } + } + + $list = getdbolist('db_orders', "coinid=$coin->id and market='poloniex'"); + foreach($list as $db_order) + { + $found = false; + foreach($orders as $order) + { + if(!isset($order['orderNumber'])) + { + debuglog($order); + continue; + } + + if($order['orderNumber'] == $db_order->uuid) + { + $found = true; + break; + } + } + + if(!$found) + { + debuglog("poloniex deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + } + + // add orders + $savebalance = getdbosql('db_balances', "name='poloniex'"); + $balances = $poloniex->get_balances(); + + foreach($balances as $symbol=>$balance) + { + if(!$balance) continue; + if($symbol == 'BTC') + { + $savebalance->balance = $balance; + $savebalance->save(); + + continue; + } + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='poloniex'"); + if($market) + { + $market->lasttraded = time(); + $market->save(); + } + + $pair = "BTC_$symbol"; + if(!isset($tickers[$pair])) continue; + + $sellprice = $tickers[$pair]['highestBid']; + if($balance * $sellprice < 0.0001) continue; + +// debuglog("poloniex selling $pair, $sellprice, $balance"); + $res = $poloniex->sell($pair, $sellprice, $balance); + + if(!isset($res['orderNumber'])) + { + debuglog($res, 5); + continue; + } + + if(!isset($tickers[$pair])) continue; + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin) continue; + + $db_order = new db_orders; + $db_order->market = 'poloniex'; + $db_order->coinid = $coin->id; + $db_order->amount = $balance; + $db_order->price = $sellprice; + $db_order->ask = $tickers[$pair]['lowestAsk']; + $db_order->bid = $tickers[$pair]['highestBid']; + $db_order->uuid = $res['orderNumber']; + $db_order->created = time(); + $db_order->save(); + } + + if($savebalance->balance >= 0.2) + { + $amount = $savebalance->balance; // - 0.0002; + debuglog("poloniex withdraw $amount to 14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9"); + + sleep(1); + + $res = $poloniex->withdraw('BTC', $amount, '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'); + debuglog($res); + + if($res && $res->success) + { + $withdraw = new db_withdraws; + $withdraw->market = 'poloniex'; + $withdraw->address = '14LS7Uda6EZGXLtRrFEZ2kWmarrxobkyu9'; + $withdraw->amount = $amount; + $withdraw->time = time(); + // $withdraw->uuid = $res->result->uuid; + $withdraw->save(); + } + } + +// debuglog('-------------- doPoloniexTrading() done'); +} + + + + + diff --git a/web/yaamp/core/trading/trading.php b/web/yaamp/core/trading/trading.php new file mode 100644 index 0000000..4da526f --- /dev/null +++ b/web/yaamp/core/trading/trading.php @@ -0,0 +1,11 @@ +dontsell) continue; + $pair = strtolower("{$coin->symbol}_btc"); + + $orders = yobit_api_query2('ActiveOrders', array('pair'=>$pair)); + if(isset($orders['return'])) foreach($orders['return'] as $uuid=>$order) + { + $ticker = yobit_api_query("ticker/$pair"); + if(!$ticker) continue; + + if($order['rate'] > $ticker->$pair->sell + 0.00000005 || $flushall) + { +// debuglog("yobit cancel order for $pair $uuid"); + $res = yobit_api_query2('CancelOrder', array('order_id'=>$uuid)); + + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$uuid)); + if($db_order) $db_order->delete(); + + sleep(1); + } + + else + { + $db_order = getdbosql('db_orders', "uuid=:uuid", array(':uuid'=>$uuid)); + if($db_order) continue; + + debuglog("yobit adding order $coin->symbol"); + + $db_order = new db_orders; + $db_order->market = 'yobit'; + $db_order->coinid = $coin->id; + $db_order->amount = $order['amount']; + $db_order->price = $order['rate']; + $db_order->ask = $ticker->$pair->sell; + $db_order->bid = $ticker->$pair->buy; + $db_order->uuid = $uuid; + $db_order->created = time(); + $db_order->save(); + } + } + + $list = getdbolist('db_orders', "coinid=$coin->id and market='yobit'"); + foreach($list as $db_order) + { + $found = false; + if(isset($orders['return'])) foreach($orders['return'] as $uuid=>$order) + { + if($uuid == $db_order->uuid) + { + $found = true; + break; + } + } + + if(!$found) + { + debuglog("yobit deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + } + + sleep(2); + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + $savebalance = getdbosql('db_balances', "name='yobit'"); + if(!$savebalance) return; + + $savebalance->balance = 0; + + $balances = yobit_api_query2('getInfo'); + if(!$balances || !isset($balances['return'])) return; + + foreach($balances['return']['funds'] as $symbol=>$amount) + { +// debuglog("$symbol, $amount"); + $amount -= 0.0001; + if($amount<=0) continue; + if($symbol == 'btc') + { + $savebalance->balance = $amount; + continue; + } + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if(!$coin || $coin->dontsell) continue; + + $market = getdbosql('db_markets', "coinid=$coin->id and name='yobit'"); + if($market) + { + $market->lasttraded = time(); + $market->save(); + } + + if($amount*$coin->price < 0.00001000) continue; + $pair = "{$symbol}_btc"; + + $data = yobit_api_query("depth/$pair?limit=11"); + if(!$data) continue; + + $sold_amount = 0; + for($i = 0; $i < 10 && $amount >= 0; $i++) + { + if(!isset($data->$pair->bids[$i])) break; + + $nextbuy = $data->$pair->bids[$i]; + if($amount*1.1 < $nextbuy[1]) break; + + $sellprice = bitcoinvaluetoa($nextbuy[0]); + $sellamount = min($amount, $nextbuy[1]); + + if($sellamount*$sellprice < 0.00010000) continue; + + debuglog("yobit selling market $pair, $sellamount, $sellprice"); + $res = yobit_api_query2('Trade', array('pair'=>$pair, 'type'=>'sell', 'rate'=>$sellprice, 'amount'=>$sellamount)); + + if(!$res || !$res['success']) + { + debuglog($res); + break; + } + + $amount -= $sellamount; + $sold_amount += $sellamount; + +// sleep(1); + } + + $ticker = yobit_api_query("ticker/$pair"); + if(!$ticker) continue; + +// if(!$coin->sellonbid && $sold_amount*$coin->price > 0.002) +// { +// sleep(5); + +// $buyprice = bitcoinvaluetoa($ticker->$pair->sell); +// $buyamount = bitcoinvaluetoa(0.00011/$ticker->$pair->sell); + +// debuglog("yobit buyback $pair, $buyamount, $buyprice"); +// $res = yobit_api_query2('Trade', array('pair'=>$pair, 'type'=>'buy', 'rate'=>$buyprice, 'amount'=>$buyamount)); + +// sleep(5); +// } + + if($amount <= 0) continue; + + if($coin->sellonbid) + $sellprice = bitcoinvaluetoa($ticker->$pair->buy); + else + $sellprice = bitcoinvaluetoa($ticker->$pair->sell); + if($amount*$sellprice < 0.00010000) continue; + +// debuglog("yobit selling $pair, $amount, $sellprice"); + + $res = yobit_api_query2('Trade', array('pair'=>$pair, 'type'=>'sell', 'rate'=>$sellprice, 'amount'=>$amount)); + if(!$res || !$res['success']) + { + debuglog($res); + continue; + } + + $db_order = new db_orders; + $db_order->market = 'yobit'; + $db_order->coinid = $coin->id; + $db_order->amount = $amount; + $db_order->price = $sellprice; + $db_order->ask = $ticker->$pair->sell; + $db_order->bid = $ticker->$pair->buy; + $db_order->uuid = $res['return']['order_id']; + $db_order->created = time(); + $db_order->save(); + + sleep(1); + } + + $savebalance->save(); + +} + + + + + + + + + + + + + + + + + diff --git a/web/yaamp/include.php b/web/yaamp/include.php new file mode 100644 index 0000000..33d4336 --- /dev/null +++ b/web/yaamp/include.php @@ -0,0 +1,11 @@ +symbol2)) + return $this->symbol2; + else + return $this->symbol; + } + + +} + diff --git a/web/yaamp/models/db_connectionsModel.php b/web/yaamp/models/db_connectionsModel.php new file mode 100644 index 0000000..9a3e1dd --- /dev/null +++ b/web/yaamp/models/db_connectionsModel.php @@ -0,0 +1,35 @@ +address"); + + public function actionStatus() + { + if(!LimitRequest('api-status', 10)) return; + + echo "{"; + foreach(yaamp_get_algos() as $i=>$algo) + { + if($i) echo ", "; + + $coins = controller()->memcache->get_database_count_ex("api_status_coins-$algo", + 'db_coins', "enable and visible and auto_ready and algo=:algo", array(':algo'=>$algo)); + + $hashrate = controller()->memcache->get_database_scalar("api_status_hashrate-$algo", + "select hashrate from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $price = controller()->memcache->get_database_scalar("api_status_price-$algo", + "select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $price = bitcoinvaluetoa(take_yaamp_fee($price/1000, $algo)); + + $rental = controller()->memcache->get_database_scalar("api_status_price-$algo", + "select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $rental = bitcoinvaluetoa($rental); + + $t = time() - 24*60*60; + + $avgprice = controller()->memcache->get_database_scalar("api_status_avgprice-$algo", + "select avg(price) from hashrate where algo=:algo and time>$t", array(':algo'=>$algo)); + + $avgprice = bitcoinvaluetoa(take_yaamp_fee($avgprice/1000, $algo)); + + $total1 = controller()->memcache->get_database_scalar("api_status_total-$algo", + "select sum(amount*price) from blocks where category!='orphan' and time>$t and algo=:algo", array(':algo'=>$algo)); + + $hashrate1 = controller()->memcache->get_database_scalar("api_status_avghashrate-$algo", + "select avg(hashrate) from hashrate where time>$t and algo=:algo", array(':algo'=>$algo)); + +// $btcmhday1 = $hashrate1 != 0? bitcoinvaluetoa($total1 / $hashrate1 * 1000000): '0.00000000'; + if($algo == 'sha256') + $btcmhday1 = $hashrate1 != 0? mbitcoinvaluetoa($total1 / $hashrate1 * 1000000 * 1000000): '0'; + else + $btcmhday1 = $hashrate1 != 0? mbitcoinvaluetoa($total1 / $hashrate1 * 1000000 * 1000): '0'; + + $fees = yaamp_fee($algo); + $port = getAlgoPort($algo); + if($port == '-') $port = 0; + + echo "\"$algo\": "; + echo "{"; + echo "\"name\": \"$algo\", "; + echo "\"port\": $port, "; + echo "\"coins\": $coins, "; + echo "\"fees\": $fees, "; + echo "\"hashrate\": $hashrate, "; + echo "\"estimate_current\": $price, "; + echo "\"estimate_last24h\": $avgprice, "; + echo "\"actual_last24h\": $btcmhday1, "; + echo "\"rental_current\": $rental"; + echo "}"; + } + + echo "}"; + } + + public function actionWallet() + { + if(!LimitRequest('api-wallet', 10)) return; + + $wallet = getparam('address'); + $user = getuserparam($wallet); + if(!$user || $user->is_locked) return; + + $total_unsold = yaamp_convert_earnings_user($user, "status!=2"); + + $total_paid = bitcoinvaluetoa(controller()->memcache->get_database_scalar("api_wallet_paid-$user->id", + "select sum(amount) from payouts where account_id=$user->id")); + + $balance = bitcoinvaluetoa($user->balance); + $total_unpaid = bitcoinvaluetoa($balance + $total_unsold); + $total_earned = bitcoinvaluetoa($total_unpaid + $total_paid); + + $coin = getdbo('db_coins', $user->coinid); + if(!$coin) return; + + echo "{"; + echo "\"currency\": \"$coin->symbol\", "; + echo "\"unsold\": $total_unsold, "; + echo "\"balance\": $balance, "; + echo "\"unpaid\": $total_unpaid, "; + echo "\"paid\": $total_paid, "; + echo "\"total\": $total_earned"; + echo "}"; + } + + public function actionWalletEx() + { + if(!LimitRequest('api-wallet', 10)) return; + + $wallet = getparam('address'); + $user = getuserparam($wallet); + if(!$user || $user->is_locked) return; + + $total_unsold = yaamp_convert_earnings_user($user, "status!=2"); + + $total_paid = bitcoinvaluetoa(controller()->memcache->get_database_scalar("api_wallet_paid-$user->id", + "select sum(amount) from payouts where account_id=$user->id")); + + $balance = bitcoinvaluetoa($user->balance); + $total_unpaid = bitcoinvaluetoa($balance + $total_unsold); + $total_earned = bitcoinvaluetoa($total_unpaid + $total_paid); + + $coin = getdbo('db_coins', $user->coinid); + if(!$coin) return; + + echo "{"; + echo "\"currency\": \"$coin->symbol\", "; + echo "\"unsold\": $total_unsold, "; + echo "\"balance\": $balance, "; + echo "\"unpaid\": $total_unpaid, "; + echo "\"paid\": $total_paid, "; + echo "\"total\": $total_earned, "; + + echo "\"miners\": "; + echo "["; + + $workers = getdbolist('db_workers', "userid=$user->id order by password"); + foreach($workers as $i=>$worker) + { + $user_rate1 = yaamp_worker_rate($worker->id, $worker->algo); + $user_rate1_bad = yaamp_worker_rate_bad($worker->id, $worker->algo); + + if($i) echo ", "; + + echo "{"; + echo "\"version\": \"$worker->version\", "; + echo "\"password\": \"$worker->password\", "; + echo "\"ID\": \"$worker->worker\", "; + echo "\"algo\": \"$worker->algo\", "; + echo "\"difficulty\": $worker->difficulty, "; + echo "\"subscribe\": $worker->subscribe, "; + echo "\"accepted\": $user_rate1, "; + echo "\"rejected\": $user_rate1_bad"; + echo "}"; + } + + echo "]"; + echo "}"; + } + + public function actionRental() + { + if(!LimitRequest('api-rental', 10)) return; + + $key = getparam('key'); + $renter = getdbosql('db_renters', "apikey=:apikey", array(':apikey'=>$key)); + if(!$renter) return; + + $balance = bitcoinvaluetoa($renter->balance); + $unconfirmed = bitcoinvaluetoa($renter->unconfirmed); + + echo "{"; + echo "\"balance\": $balance, "; + echo "\"unconfirmed\": $unconfirmed, "; + + echo "\"jobs\": ["; + $list = getdbolist('db_jobs', "renterid=$renter->id"); + foreach($list as $i=>$job) + { + if($i) echo ", "; + + $hashrate = yaamp_job_rate($job->id); + $hashrate_bad = yaamp_job_rate_bad($job->id); + + echo '{'; + echo "\"jobid\": \"$job->id\", "; + echo "\"algo\": \"$job->algo\", "; + echo "\"price\": \"$job->price\", "; + echo "\"hashrate\": \"$job->speed\", "; + echo "\"server\": \"$job->host\", "; + echo "\"port\": \"$job->port\", "; + echo "\"username\": \"$job->username\", "; + echo "\"password\": \"$job->password\", "; + echo "\"started\": \"$job->ready\", "; + echo "\"active\": \"$job->active\", "; + echo "\"accepted\": \"$hashrate\", "; + echo "\"rejected\": \"$hashrate_bad\", "; + echo "\"diff\": \"$job->difficulty\""; + + echo '}'; + } + + echo "]}"; + } + + public function actionRental_price() + { + $key = getparam('key'); + $renter = getdbosql('db_renters', "apikey=:apikey", array(':apikey'=>$key)); + if(!$renter) return; + + $jobid = getparam('jobid'); + $price = getparam('price'); + + $job = getdbo('db_jobs', $jobid); + if($job->renterid != $renter->id) return; + + $job->price = $price; + $job->time = time(); + $job->save(); + } + + public function actionRental_hashrate() + { + $key = getparam('key'); + $renter = getdbosql('db_renters', "apikey=:apikey", array(':apikey'=>$key)); + if(!$renter) return; + + $jobid = getparam('jobid'); + $hashrate = getparam('hashrate'); + + $job = getdbo('db_jobs', $jobid); + if($job->renterid != $renter->id) return; + + $job->speed = $hashrate; + $job->time = time(); + $job->save(); + } + + public function actionRental_start() + { + $key = getparam('key'); + $renter = getdbosql('db_renters', "apikey=:apikey", array(':apikey'=>$key)); + if(!$renter || $renter->balance<=0) return; + + $jobid = getparam('jobid'); + + $job = getdbo('db_jobs', $jobid); + if($job->renterid != $renter->id) return; + + $job->ready = true; + $job->time = time(); + $job->save(); + } + + public function actionRental_stop() + { + $key = getparam('key'); + $renter = getdbosql('db_renters', "apikey=:apikey", array(':apikey'=>$key)); + if(!$renter) return; + + $jobid = getparam('jobid'); + + $job = getdbo('db_jobs', $jobid); + if($job->renterid != $renter->id) return; + + $job->ready = false; + $job->time = time(); + $job->save(); + } + +// public function actionNodeReport() +// { +// $name = getparam('name'); +// $uptime = getparam('uptime'); + +// $server = getdbosql('db_servers', "name='$name'"); +// if(!$server) +// { +// $server = new db_servers; +// $server->name = $name; +// } + +// $server->uptime = $uptime; +// $server->save(); +// } + +} + +// function dummy() +// { +// $uptime = system('uptime'); +// $name = system('hostname'); + +// fetch_url("http://yaamp.com/api/nodereport?name=$name&uptime=$uptime"); +// } + + + + + diff --git a/web/yaamp/modules/coin/CoinController.php b/web/yaamp/modules/coin/CoinController.php new file mode 100644 index 0000000..8fa1df5 --- /dev/null +++ b/web/yaamp/modules/coin/CoinController.php @@ -0,0 +1,54 @@ +admin) return; + $this->render('index'); + } + + public function actionCreate() + { + if(!$this->admin) return; + $coin = new db_coins; + $coin->txmessage = true; + $coin->created = time(); + + if(isset($_POST['db_coins'])) + { + $coin->attributes = $_POST['db_coins']; + if($coin->save()) + $this->redirect(array('index')); + } + + $this->render('_form', array('coin'=>$coin, 'update'=>false)); + } + + public function actionUpdate() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', getiparam('id')); + + if(isset($_POST['db_coins'])) + { + $coin->attributes = $_POST['db_coins']; + if($coin->save()) + $this->redirect(array('index')); + } + + $this->render('_form', array('coin'=>$coin, 'update'=>true)); + } + +} + + + + + + + diff --git a/web/yaamp/modules/coin/_form.php b/web/yaamp/modules/coin/_form.php new file mode 100644 index 0000000..70d0509 --- /dev/null +++ b/web/yaamp/modules/coin/_form.php @@ -0,0 +1,75 @@ +Back to coin list"; + +$this->widget('UniForm'); + +echo CUFHtml::beginForm(); +echo CUFHtml::errorSummary($coin); +echo CUFHtml::openTag('fieldset', array('class'=>'inlineLabels')); + +if(!$coin->installed) +{ + echo CUFHtml::openActiveCtrlHolder($coin, 'name'); + echo CUFHtml::activeLabelEx($coin, 'name'); + echo CUFHtml::activeTextField($coin, 'name'); + echo "

    "; + echo CUFHtml::closeCtrlHolder(); + + echo CUFHtml::openActiveCtrlHolder($coin, 'symbol'); + echo CUFHtml::activeLabelEx($coin, 'symbol'); + echo CUFHtml::activeTextField($coin, 'symbol'); + echo "

    "; + echo CUFHtml::closeCtrlHolder(); + + echo CUFHtml::openActiveCtrlHolder($coin, 'algo'); + echo CUFHtml::activeLabelEx($coin, 'algo'); + echo CUFHtml::activeTextField($coin, 'algo'); + echo "

    "; + echo CUFHtml::closeCtrlHolder(); + + echo CUFHtml::openActiveCtrlHolder($coin, 'image'); + echo CUFHtml::activeLabelEx($coin,'image'); + echo CUFHtml::activeTextField($coin, 'image'); + echo "

    "; + echo CUFHtml::closeCtrlHolder(); +} + +echo CUFHtml::openActiveCtrlHolder($coin, 'errors'); +echo CUFHtml::activeLabelEx($coin,'errors'); +echo CUFHtml::activeTextField($coin, 'errors'); +echo "

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'link_bitcointalk'); +echo CUFHtml::activeLabelEx($coin, 'link_bitcointalk'); +echo CUFHtml::activeTextField($coin, 'link_bitcointalk'); +echo "

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'link_github'); +echo CUFHtml::activeLabelEx($coin, 'link_github'); +echo CUFHtml::activeTextField($coin, 'link_github'); +echo "

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'link_exchange'); +echo CUFHtml::activeLabelEx($coin, 'link_exchange'); +echo CUFHtml::activeTextField($coin, 'link_exchange'); +echo "

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'link_explorer'); +echo CUFHtml::activeLabelEx($coin, 'link_explorer'); +echo CUFHtml::activeTextField($coin, 'link_explorer'); +echo "

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::closeTag('fieldset'); +showSubmitButton($update? 'Save': 'Create'); +echo CUFHtml::endForm(); + +echo "




    "; +echo "




    "; + + diff --git a/web/yaamp/modules/coin/index.php b/web/yaamp/modules/coin/index.php new file mode 100644 index 0000000..024f01c --- /dev/null +++ b/web/yaamp/modules/coin/index.php @@ -0,0 +1,134 @@ +Add a coin"; +echo '
    '; + +showTableSorter('maintable', '{headers: {0: {sorter: false}}}'); +echo ""; + +echo ""; +echo ""; +echo "Name"; +echo "Symbol"; +echo "Algo"; +echo "Status"; +echo "Version"; +echo "Created"; +//echo "Difficulty"; +echo "Height"; +echo "Message"; +echo "Links"; +echo ""; +echo ""; + +$total_active = 0; +$total_installed = 0; + +$coins = getdbolist('db_coins', "1 order by id desc"); +foreach($coins as $coin) +{ +// if($coin->symbol == 'BTC') continue; + if($coin->enable) $total_active++; + if($coin->installed) $total_installed++; + + $coin->errors = substr($coin->errors, 0, 30); + $coin->version = substr($coin->version, 0, 20); + $difficulty = Itoa2($coin->difficulty, 3); + $d = datetoa2($coin->created); + + echo ""; + echo ""; + + echo "$coin->name"; + + if($this->admin) + echo "$coin->symbol"; + else + echo "$coin->symbol"; + + echo "$coin->algo"; + + if($coin->enable) + echo "running"; + + else if($coin->installed) + echo "installed"; + + else + echo ""; + + echo "$coin->version"; + echo "$d ago"; + +// echo "$difficulty"; + echo "$coin->block_height"; + + echo "$coin->errors"; + echo ""; + + if(!empty($coin->link_bitcointalk)) + echo "forum "; + + if(!empty($coin->link_github)) + echo "git "; + +// if(!empty($coin->link_explorer)) +// echo "expl "; + + echo "google "; + +// if(!empty($coin->link_exchange)) +// echo "exch "; + + $list2 = getdbolist('db_markets', "coinid=$coin->id"); + foreach($list2 as $market) + { + $url = ''; + $lowsymbol = strtolower($coin->symbol); + + if($market->name == 'cryptsy') + $url = "https://www.cryptsy.com/markets/view/{$coin->symbol}_BTC"; + + else if($market->name == 'bittrex') + $url = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($market->name == 'mintpal') + $url = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($market->name == 'poloniex') + $url = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($market->name == 'c-cex') + $url = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($market->name == 'bleutrade') + $url = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else if($market->name == 'yobit') + $url = "https://yobit.net/en/trade/$coin->symbol/BTC"; + + echo "$market->name "; + } + + + echo ""; + echo ""; +} + +echo ""; + +$total = count($coins); + +echo ""; +echo ""; +echo "$total coins, $total_installed installed, $total_active running"; +echo ""; + +echo ""; + +echo "




    "; +echo "




    "; + + + + diff --git a/web/yaamp/modules/common/CommonController.php b/web/yaamp/modules/common/CommonController.php new file mode 100644 index 0000000..b699ece --- /dev/null +++ b/web/yaamp/modules/common/CommonController.php @@ -0,0 +1,55 @@ +memcache = new YaampMemcache; + $this->t1 = microtime(true); + + if(user()->getState('yaamp_admin')) + $this->admin = true; + + $algo = user()->getState('yaamp-algo'); + if(!$algo) user()->setState('yaamp-algo', 'x11'); + + return true; + } + + public function afterAction($action) + { + // debuglog("after action"); + + $t2 = microtime(true); + $d1 = $t2 - $this->t1; + + $url = "$this->id/{$this->action->id}"; + $this->memcache->add_monitoring_function($url, $d1); + } + + public function actionMaintenance() + { + $this->render('maintenance'); + } + +} + + + + + + + diff --git a/web/yaamp/modules/common/maintenance.php b/web/yaamp/modules/common/maintenance.php new file mode 100644 index 0000000..bcc5ed5 --- /dev/null +++ b/web/yaamp/modules/common/maintenance.php @@ -0,0 +1,25 @@ + +
    + +
    +
    SITE UNDER MAINTENANCE
    +
    + +
    + +Will be back soon.

    + +The pool server is still running normally. + +




    + +
    +
    + +









    +









    +









    +









    +









    +









    + diff --git a/web/yaamp/modules/common/yaamp.php b/web/yaamp/modules/common/yaamp.php new file mode 100644 index 0000000..6ff1eb0 --- /dev/null +++ b/web/yaamp/modules/common/yaamp.php @@ -0,0 +1,23 @@ + +
    + +
    +
    WRONG SITE
    +
    + +
    + +GOTO YAAMP.COM + +




    + +
    +
    + +









    +









    +









    +









    +









    +









    + diff --git a/web/yaamp/modules/explorer/ExplorerController.php b/web/yaamp/modules/explorer/ExplorerController.php new file mode 100644 index 0000000..158f09e --- /dev/null +++ b/web/yaamp/modules/explorer/ExplorerController.php @@ -0,0 +1,56 @@ +0) + { + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + $hash = $remote->getblockhash(intval($height)); + } + + else + $hash = getparam('hash'); + + $txid = getparam('txid'); + if($coin && !empty($txid) && ctype_alnum($txid)) + { + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + $tx = $remote->getrawtransaction($txid, 1); + + $hash = $tx['blockhash']; + } + + if($coin && !empty($hash) && ctype_alnum($hash)) + $this->render('block', array('coin'=>$coin, 'hash'=>substr($hash, 0, 64))); + + else if($coin) + $this->render('coin', array('coin'=>$coin)); + + else + $this->render('index'); + } + + +} + + + + + + + diff --git a/web/yaamp/modules/explorer/block.php b/web/yaamp/modules/explorer/block.php new file mode 100644 index 0000000..fd570f9 --- /dev/null +++ b/web/yaamp/modules/explorer/block.php @@ -0,0 +1,122 @@ +rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + +$block = $remote->getblock($hash); +if(!$block) return; +//debuglog($block); + +$d = date('Y-m-d H:i:s', $block['time']); +$confirms = isset($block['confirmations'])? $block['confirmations']: ''; +$txcount = count($block['tx']); + +$version = dechex($block['version']); +$nonce = dechex($block['nonce']); + +echo ""; +echo ""; + +echo ""; +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo ""; +echo ""; + +echo ""; +echo ""; + +if(isset($block['previousblockhash'])) + echo ""; + +if(isset($block['nextblockhash'])) + echo ""; + +echo ""; + +echo "
    Coin:$coin->name
    Blockhash:$hash
    Confirmations:$confirms
    Size:{$block['size']} bytes
    Height:{$block['height']}
    Time:$d
    Difficulty:{$block['difficulty']}
    Version:$version
    Merkle Root:{$block['merkleroot']}
    Nonce:$nonce
    Bits:{$block['bits']}
    Previous Hash: + {$block['previousblockhash']}
    Next Hash: + {$block['nextblockhash']}
    Transactions:$txcount

    "; + +//////////////////////////////////////////////////////////////////////////////// + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($block['tx'] as $txhash) +{ + $tx = $remote->getrawtransaction($txhash, 1); + if(!$tx) continue; + + $valuetx = 0; + foreach($tx['vout'] as $vout) + $valuetx += $vout['value']; + + echo ""; + + echo ""; + echo ""; + + echo ""; + + echo ""; + + echo ""; +} + +echo "
    Transaction HashValueFrom (amount)To (amount)
    {$tx['txid']}$valuetx"; + foreach($tx['vin'] as $vin) + { + if(isset($vin['coinbase'])) + echo "Generation"; + + } + echo ""; + foreach($tx['vout'] as $vout) + { + $value = $vout['value']; + + if(isset($vout['scriptPubKey']['addresses'][0])) + echo "{$vout['scriptPubKey']['addresses'][0]} ($value)"; + else + echo "($value)"; + + echo '
    '; + } + echo "
    "; + +echo << + + + + +end; + +echo '









    '; +echo '









    '; +echo '









    '; + + diff --git a/web/yaamp/modules/explorer/coin.php b/web/yaamp/modules/explorer/coin.php new file mode 100644 index 0000000..89312a1 --- /dev/null +++ b/web/yaamp/modules/explorer/coin.php @@ -0,0 +1,63 @@ +"; +echo "
    "; +echo "
    $coin->name Explorer
    "; +echo "
    "; + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); +for($i = $coin->block_height; $i > $coin->block_height-25; $i--) +{ + $hash = $remote->getblockhash($i); + if(!$hash) continue; + + $block = $remote->getblock($hash); + if(!$block) continue; + + $d = datetoa2($block['time']); + $confirms = isset($block['confirmations'])? $block['confirmations']: ''; + $tx = count($block['tx']); + $diff = $block['difficulty']; + +// debuglog($block); + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; +} + +echo "
    TimeHeightDiffTransactionsConfirmationsBlockhash
    $d$i$diff$tx$confirms$hash
    "; + +echo << + + + + +end; + +echo '









    '; +echo '









    '; +echo '









    '; + + + + diff --git a/web/yaamp/modules/explorer/index.php b/web/yaamp/modules/explorer/index.php new file mode 100644 index 0000000..274016f --- /dev/null +++ b/web/yaamp/modules/explorer/index.php @@ -0,0 +1,67 @@ +"; +echo "
    "; +echo "
    Block Explorer
    "; +echo "
    "; + +showTableSorter('maintable', '{headers: {0: {sorter: false}, 9: {sorter: false}}}'); +echo ""; + +echo ""; +echo ""; +echo "Name"; +echo "Symbol"; +echo "Algo"; +echo "Version"; +echo "Height"; +echo "Difficulty"; +echo "Connections"; +echo "Network Hash"; +echo ""; +echo ""; +echo ""; + +$list = getdbolist('db_coins', "enable and visible order by name"); +foreach($list as $coin) +{ + if($coin->symbol == 'BTC') continue; + if(!empty($coin->symbol2)) continue; + + $coin->version = substr($coin->version, 0, 20); + $difficulty = Itoa2($coin->difficulty, 3); + $nethash = $coin->network_hash? Itoa2($coin->network_hash).'h': ''; + + echo ""; + echo ""; + + echo "$coin->name"; + echo "$coin->symbol"; + + echo "$coin->algo"; + echo "$coin->version"; + + echo "$coin->block_height"; + echo "$difficulty"; + echo "$coin->connections"; + echo "$nethash"; + + echo ""; + + if(!empty($coin->link_bitcointalk)) + echo "forum "; + + echo ""; + echo ""; +} + +echo ""; +echo ""; + +echo "
    "; + +echo '









    '; +echo '









    '; +echo '









    '; + + diff --git a/web/yaamp/modules/explorer/tx.php b/web/yaamp/modules/explorer/tx.php new file mode 100644 index 0000000..ca0b9eb --- /dev/null +++ b/web/yaamp/modules/explorer/tx.php @@ -0,0 +1,73 @@ +rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$tx = $remote->getrawtransaction($txhash, 1); +if(!$tx) continue; + +$valuetx = 0; +foreach($tx['vout'] as $vout) + $valuetx += $vout['value']; + +echo ""; + +echo ""; +echo ""; + +echo ""; + +echo ""; + +echo ""; + +echo "
    Transaction HashValueFrom (amount)To (amount)
    {$tx['txid']}$valuetx"; +foreach($tx['vin'] as $vin) +{ + if(isset($vin['coinbase'])) + echo "Generation"; + +} +echo ""; +foreach($tx['vout'] as $vout) +{ + $value = $vout['value']; + + if(isset($vout['scriptPubKey']['addresses'][0])) + echo "{$vout['scriptPubKey']['addresses'][0]} ($value)"; + else + echo "($value)"; + + echo '
    '; +} +echo "
    "; + +echo << + + + + +end; + +echo '









    '; +echo '









    '; +echo '









    '; + + + + + + + + diff --git a/web/yaamp/modules/explorer/util.php b/web/yaamp/modules/explorer/util.php new file mode 100644 index 0000000..feb25c0 --- /dev/null +++ b/web/yaamp/modules/explorer/util.php @@ -0,0 +1,146 @@ +hexdec($addressversion)) + { + return false; + } + $check=substr($addr,0,strlen($addr)-8); + $check=pack("H*" , $check); + $check=strtoupper(hash("sha256",hash("sha256",$check,true))); + $check=substr($check,0,8); + return $check==substr($addr,strlen($addr)-8); +} + +function hash160($data) +{ + $data=pack("H*" , $data); + return strtoupper(hash("ripemd160",hash("sha256",$data,true))); +} + +function pubKeyToAddress($pubkey) +{ + return hash160ToAddress(hash160($pubkey)); +} + +function remove0x($string) +{ + if(substr($string,0,2)=="0x"||substr($string,0,2)=="0X") + { + $string=substr($string,2); + } + return $string; +} + diff --git a/web/yaamp/modules/market/MarketController.php b/web/yaamp/modules/market/MarketController.php new file mode 100644 index 0000000..53c0b17 --- /dev/null +++ b/web/yaamp/modules/market/MarketController.php @@ -0,0 +1,86 @@ +admin) return; + + $market = getdbo('db_markets', getiparam('id')); + $coin = getdbo('db_coins', $market->coinid); + + if(isset($_POST['db_markets'])) + { + $market->attributes = $_POST['db_markets']; + if($market->save()) + $this->redirect(array('site/coin', 'id'=>$coin->id)); + } + + $this->render('update', array('market'=>$market, 'coin'=>$coin)); + } + + public function actionDelete() + { + if(!$this->admin) return; + + $market = getdbo('db_markets', getiparam('id')); + $coin = getdbo('db_coins', $market->coinid); + + if($market) $market->delete(); + $this->redirect(array('site/coin', 'id'=>$coin->id)); + } + + public function actionSellto() + { + if(!$this->admin) return; + + $market = getdbo('db_markets', getiparam('id')); + $coin = getdbo('db_coins', $market->coinid); + $amount = getparam('amount'); + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $info = $remote->getinfo(); + if(!$info || !$info['balance']) return false; + + $deposit_info = $remote->validateaddress($market->deposit_address); + if(!$deposit_info || !isset($deposit_info['isvalid']) || !$deposit_info['isvalid']) + { + user()->setFlash('error', "invalid address $coin->name, $market->deposit_address"); + $this->redirect(array('site/coin', 'id'=>$coin->id)); + } + + $amount = min($amount, $info['balance'] - $info['paytxfee']); +// $amount = max($amount, $info['balance'] - $info['paytxfee']); + $amount = round($amount, 8); + + debuglog("selling ($market->deposit_address, $amount)"); + + $tx = $remote->sendtoaddress($market->deposit_address, $amount); + if(!$tx) + { + user()->setFlash('error', $remote->error); + $this->redirect(array('site/coin', 'id'=>$coin->id)); + } + + $exchange = new db_exchange; + $exchange->market = $market->name; + $exchange->coinid = $coin->id; + $exchange->send_time = time(); + $exchange->quantity = $amount; + $exchange->price_estimate = $coin->price; + $exchange->status = 'waiting'; + $exchange->tx = $tx; + $exchange->save(); + + $this->redirect(array('site/coin', 'id'=>$coin->id)); + } + +} + + + + + + + diff --git a/web/yaamp/modules/market/update.php b/web/yaamp/modules/market/update.php new file mode 100644 index 0000000..11b0f90 --- /dev/null +++ b/web/yaamp/modules/market/update.php @@ -0,0 +1,23 @@ +id'>$coin->name "; +echo "$market->name
    "; + +$this->widget('UniForm'); + +echo CUFHtml::beginForm(); +echo CUFHtml::errorSummary($market); +echo CUFHtml::openTag('fieldset', array('class'=>'inlineLabels')); + +echo CUFHtml::openActiveCtrlHolder($market, 'deposit_address'); +echo CUFHtml::activeLabelEx($market, 'deposit_address'); +echo CUFHtml::activeTextField($market, 'deposit_address', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::closeTag('fieldset'); +showSubmitButton('Save'); +echo CUFHtml::endForm(); + + + diff --git a/web/yaamp/modules/nicehash/NicehashController.php b/web/yaamp/modules/nicehash/NicehashController.php new file mode 100644 index 0000000..ba9be06 --- /dev/null +++ b/web/yaamp/modules/nicehash/NicehashController.php @@ -0,0 +1,55 @@ +admin) return; + $this->render('index'); + } + + public function actionIndex_results() + { + if(!$this->admin) return; + $this->renderPartial('index_results'); + } + + public function actionStart() + { + if(!$this->admin) return; + $id = getiparam('id'); + + $nicehash = getdbo('db_nicehash', $id); + if(!$nicehash) return; + + $nicehash->active = true; + $nicehash->save(); + + $this->goback(); + } + + public function actionStop() + { + if(!$this->admin) return; + $id = getiparam('id'); + + $nicehash = getdbo('db_nicehash', $id); + if(!$nicehash) return; + + $nicehash->active = false; + $nicehash->save(); + + $this->goback(); + } + + +} + + + + + + + diff --git a/web/yaamp/modules/nicehash/index.php b/web/yaamp/modules/nicehash/index.php new file mode 100644 index 0000000..e1cd63a --- /dev/null +++ b/web/yaamp/modules/nicehash/index.php @@ -0,0 +1,37 @@ +"; +echo "









    "; +echo "
    "; + +echo "









    "; +echo "









    "; +echo "









    "; +echo "









    "; + +echo << + +function page_refresh() +{ + index_refresh(); +} + +function index_ready(data) +{ + $('#index_results').html(data); +} + +function index_refresh() +{ + var url = "/nicehash/index_results"; + $.get(url, '', index_ready); +} + + + +END; + diff --git a/web/yaamp/modules/nicehash/index_results.php b/web/yaamp/modules/nicehash/index_results.php new file mode 100644 index 0000000..4cac07b --- /dev/null +++ b/web/yaamp/modules/nicehash/index_results.php @@ -0,0 +1,78 @@ +result->balance_confirmed; + +echo "balance $balance
    "; + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$list = getdbolist('db_nicehash'); +foreach($list as $nicehash) +{ + $price2 = mbitcoinvaluetoa(dboscalar("select price from services where algo='$nicehash->algo'")*1000); + + $d = datetoa2($nicehash->last_decrease); + $yaamp = mbitcoinvaluetoa(dboscalar("select price from hashrate where algo='$nicehash->algo' order by time desc limit 1")); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + if($yaamp > $price2*1.1) + echo ""; + else + echo ""; + + if($nicehash->price > $yaamp) + echo ""; + else + echo ""; + + echo ""; + echo ""; + + if(!$nicehash->workers && !$nicehash->accepted && !$nicehash->rejected) + { + echo ""; + } + else + { + echo ""; + echo ""; + echo ""; + } + + if($nicehash->active) + echo ""; + else + echo ""; + + echo ""; +} + +echo "
    IDAlgoBTCNicehashYaampPriceSpeedLast DecWorkersAcceptedRejected
    $nicehash->orderid$nicehash->algo$nicehash->btc$price2$yaamp$yaamp$nicehash->price$nicehash->price$nicehash->speed$d$nicehash->workers$nicehash->accepted$nicehash->rejectedstopstart
    "; + + diff --git a/web/yaamp/modules/renting/RentingController.php b/web/yaamp/modules/renting/RentingController.php new file mode 100644 index 0000000..9bea8a3 --- /dev/null +++ b/web/yaamp/modules/renting/RentingController.php @@ -0,0 +1,469 @@ +array( + 'class'=>'CCaptchaAction', + 'backColor'=>0xeeeeee, + ), + ); + } + + private function verifyparam() + { + $deposit = user()->getState('yaamp-deposit'); + $address = getparam('address'); + + if(!$this->admin && $deposit != $address) + return false; + + return true; + } + + public function actionLogin() + { + $deposit = isset($_POST['deposit_address'])? substr($_POST['deposit_address'], 0, 34): ''; + $password = isset($_POST['deposit_password'])? substr($_POST['deposit_password'], 0, 64): ''; + + $renter = getdbosql('db_renters', "address=:address", array(':address'=>$deposit)); + if(!$renter) + { + $this->render('login'); + return; + } + + if(md5($password) != $renter->password && (!empty($renter->password) || !empty($password))) + { + user()->setFlash('error', "Login failed."); + $this->render('login'); + return; + } + +// $recents = isset($_COOKIE['deposits'])? unserialize($_COOKIE['deposits']): array(); +// $recents[$renter->address] = $renter->address; +// setcookie('deposits', serialize($recents), time()+60*60*24*30); + + user()->setState('yaamp-deposit', $renter->address); + $this->redirect("/renting"); + } + + public function actionIndex() + { + $deposit = user()->getState('yaamp-deposit'); + if(!$deposit && !$this->admin) + { + $this->render('login'); + return; + } + + $address = getparam('address'); + if($this->admin && !empty($address)) + { + $deposit = $address; + user()->setState('yaamp-deposit', $deposit); + } + + $renter = getdbosql('db_renters', "address=:deposit", array(':deposit'=>$deposit)); + if(!$renter) + { + $this->render('login'); + return; + } + + $changed = false; + if(isset($_POST['deposit_email'])) + { + $renter->email = $_POST['deposit_email']; + $changed = true; + } + + if(isset($_POST['deposit_password']) && !empty($_POST['deposit_password'])) + { + if($_POST['deposit_password'] == $_POST['deposit_confirm']) + { + $renter->password = md5($_POST['deposit_password']); + $changed = true; + } + else + { + user()->setFlash('error', "Confirm different from password."); + $this->goback(); + + return; + } + } + + if($changed) + { + debuglog("saving renter {$_SERVER['REMOTE_ADDR']} $renter->address"); + + dborun("update renters set email=:email, password=:password where id=$renter->id", + array(':email'=>$renter->email, ':password'=>$renter->password)); + +// $renter->save(); + + user()->setFlash('message', "Settings saved."); + $this->redirect("/renting"); + } + + $this->render('index', array('renter'=>$renter)); + } + + public function actionSettings() + { + $this->render('settings'); + } + + public function actionAdmin() + { + if(!$this->admin) return; + $this->render('admin'); + } + + public function actionCreate() + { + $this->render('create'); + } + + public function actionLogout() + { + user()->setState('yaamp-deposit', ''); + $this->redirect('/renting'); + } + + public function actionTx() + { + $this->renderPartial('tx'); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + + public function actionJobs_stop() + { + $job = getdbo('db_jobs', getiparam('id')); + + $renter = getdbo('db_renters', $job->renterid); + if(!$renter || $renter->address != user()->getState('yaamp-deposit')) $this->goback(); + + $job->active = false; + $job->ready = false; + $job->time = time(); +// $job->difficulty = null; + $job->save(); + + $this->goback(); + } + + public function actionJobs_start() + { + $job = getdbo('db_jobs', getiparam('id')); +// if($job->algo == 'sha256') $this->goback(); + + $renter = getdbo('db_renters', $job->renterid); + if(!$renter || $renter->balance<=0.00001000 || $renter->address != user()->getState('yaamp-deposit')) $this->goback(); + + $rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$job->algo)); + if($job->price > $rent) $job->active = true; + + $job->ready = true; + $job->time = time(); +// $job->difficulty = null; + $job->save(); + + $this->goback(); + } + + public function actionJobs_startall() + { + $deposit = user()->getState('yaamp-deposit'); + $renter = getrenterparam($deposit); + if(!$renter || $renter->balance<=0.00001000) $this->goback(); + + $list = getdbolist('db_jobs', "renterid=$renter->id"); + foreach($list as $job) + { + $rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$job->algo)); + if($job->price > $rent) $job->active = true; + + $job->ready = true; + $job->time = time(); + $job->save(); + } + + $this->goback(); + } + + public function actionJobs_stopall() + { + $deposit = user()->getState('yaamp-deposit'); + $renter = getrenterparam($deposit); + if(!$renter) $this->goback(); + + $list = getdbolist('db_jobs', "renterid=$renter->id"); + foreach($list as $job) + { + $job->active = false; + $job->ready = false; + $job->time = time(); + $job->save(); + } + + $this->goback(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + + public function actionBalance_results() + { + if(!$this->verifyparam()) return; + $this->renderPartial('balance_results'); + } + + public function actionOrders_results() + { + if(!$this->verifyparam()) return; + $this->renderPartial('orders_results'); + } + + public function actionAll_orders_results() + { + $this->renderPartial('all_orders_results'); + } + + public function actionGraph_job_results() + { + $this->renderPartial('graph_job_results'); + } + + public function actionStatus_results() + { + $this->renderPartial('status_results'); + } + + public function actionGraph_price_results() + { + $this->renderPartial('graph_price_results'); + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + + public function actionOrderDelete() + { + $job = getdbo('db_jobs', getiparam('id')); + if(!$job) return; + + $renter = getdbo('db_renters', $job->renterid); + if(!$renter || $renter->address != user()->getState('yaamp-deposit')) return; + + $job->delete(); + $this->redirect("/renting?address=$renter->address"); + } + + public function actionOrderSave() + { + $renter = getdbo('db_renters', XssFilter(getparam('order_renterid'))); + if(!$renter || $renter->address != user()->getState('yaamp-deposit')) return; + + $job = getdbo('db_jobs', XssFilter(getparam('order_id'))); + if(!$job) + { + $job = new db_jobs; + $job->renterid = getparam('order_renterid'); + } + + $job->algo = getparam('order_algo'); + $job->username = getparam('order_username'); + $job->password = getparam('order_password'); + $job->percent = getparam('order_percent'); + $job->price = getparam('order_price'); + $job->speed = getparam('order_speed')*1000000; + + if( empty($job->algo) || empty($job->username) || empty($job->password) || empty($job->price) || + empty($job->speed) || empty(getparam('order_address')) || empty(getparam('order_host'))) + { + $this->redirect('/renting'); + return; + } + + if($job->speed<100000) + { + $this->redirect('/renting'); + return; + } + + $a = explode(':', getparam('order_host')); + if(!isset($a[0]) || !isset($a[1])) + { + $this->redirect('/renting'); + return; + } + + $job->host = $a[0]; + $job->port = $a[1]; + + $rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$job->algo)); + + if($job->price > $rent && $job->ready) + $job->active = true; + + else if($job->price < $rent) + $job->active = false; + + $job->time = time(); +// $job->difficulty = null; + $job->save(); + + $this->redirect("/renting?address=".getparam('order_address')); + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + + public function actionOrderDialog() + { + $renter = getrenterparam(getparam('address')); + if(!$renter) return; + + $a = 'x11'; + $server = ''; + $username = ''; + $password = 'xx'; + $percent = ''; + $price = ''; + $speed = ''; + $id = 0; + + $job = getdbo('db_jobs', getiparam('id')); + if($job) + { + $id = $job->id; + $a = $job->algo; + $server = "$job->host:$job->port"; + $username = $job->username; + $password = $job->password; + $percent = $job->percent; + $price = mbitcoinvaluetoa($job->price); + $speed = $job->speed/1000000; + } + + echo << + + + + +

    Enter your job information below and click Submit when you are ready.

    + + + + + + + + +end; + + if(controller()->admin) + echo ""; + + echo "
    Algo:
    Server:
    Username:
    Password:
    Max Price
    (mBTC/mh/day):
    Max Hashrate
    (Mh/s):
    Percent:
    "; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public function actionResetSpent() + { + $renter = getrenterparam(getparam('address')); + if(!$renter) return; + + $renter->custom_start = 0; + $renter->spent = $renter->custom_balance; + + $renter->save(); + $this->goback(); + } + + public function actionWithdraw() + { + $fees = 0.0001; + + $deposit = user()->getState('yaamp-deposit'); + if(!$deposit) + { + $this->render('login'); + return; + } + + $renter = getrenterparam($deposit); + if(!$renter) + { + $this->render('login'); + return; + } + + $amount = getparam('withdraw_amount'); + $address = getparam('withdraw_address'); + + $amount = floatval(bitcoinvaluetoa(min($amount, $renter->balance-$fees))); + if($amount < 0.001) + { + user()->setFlash('error', 'Minimum withdraw is 0.001'); + $this->redirect("/renting"); + return; + } + + $coin = getdbosql('db_coins', "symbol='BTC'"); + if(!$coin) return; + + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + + $res = $remote->validateaddress($address); + if(!$res || !isset($res['isvalid']) || !$res['isvalid']) + { + user()->setFlash('error', 'Invalid address'); + $this->redirect("/renting"); + + return; + } + + $rentertx = new db_rentertxs; + $rentertx->renterid = $renter->id; + $rentertx->time = time(); + $rentertx->amount = $amount; + $rentertx->type = 'withdraw'; + $rentertx->address = $address; + $rentertx->tx = 'scheduled'; + $rentertx->save(); + + debuglog("withdraw scheduled $renter->id $renter->address, $amount to $address"); + + user()->setFlash('message', "withdraw scheduled"); + $this->redirect("/renting"); + } + +} + + + + + + + + diff --git a/web/yaamp/modules/renting/admin.php b/web/yaamp/modules/renting/admin.php new file mode 100644 index 0000000..74cd75f --- /dev/null +++ b/web/yaamp/modules/renting/admin.php @@ -0,0 +1,148 @@ +getState('yaamp-deposit'); +echo "refresh
    "; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +$list = getdbolist('db_rentertxs', "1 order by time desc limit 10"); +if(count($list) == 0) return; + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($list as $tx) +{ + $d = datetoa2($tx->time); + $amount = bitcoinvaluetoa($tx->amount); + + $renter = getdbo('db_renters', $tx->renterid); + if(!$renter) continue; + + echo ""; + + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + + if(strlen($tx->tx) > 32) + { + $tx_show = substr($tx->tx, 0, 36).'...'; + $txurl = "https://blockchain.info/tx/$tx->tx"; + echo ""; + } + else + echo ""; + + echo ""; +} + +echo "
    IDAddressTimeTypeAmountTx
    $renter->id$renter->address$d ago$tx->type$amount$tx_show$tx->tx

    "; + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$list = getdbolist('db_renters', "balance>0 order by balance desc"); +foreach($list as $renter) +{ + $count = dboscalar("select count(*) from jobs where renterid=$renter->id"); + $active = dboscalar("select count(*) from jobs where renterid=$renter->id and active"); + + if($deposit == $renter->address) + echo ""; + else + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; +} + +echo "
    RenterAddressEmailSpentBalanceUnconfirmedJobsActive
    $renter->id$renter->address$renter->email$renter->spent$renter->balance$renter->unconfirmed$count$active
    "; + +///////////////////////////////////////////////////////////////////////////// + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$list = getdbolist('db_jobs', "ready"); +foreach($list as $job) +{ + $hashrate = yaamp_job_rate($job->id); + $hashrate = $hashrate? Itoa2($hashrate).'h/s': ''; + + $speed = Itoa2($job->speed).'h/s'; + + $renter = getdbo('db_renters', $job->renterid); + if(!$renter) continue; + + if($deposit == $renter->address) + echo ""; + else + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; +} + +echo "
    RenterJobAddressAlgoHostMax PriceMax HashCurrent HashDifficultyReadyActive
    $job->renterid$job->id$renter->address$job->algo$job->host:$job->port$job->price$speed$hashrate$job->difficulty$job->ready$job->active
    "; + +echo "









    "; + + diff --git a/web/yaamp/modules/renting/all_orders_results.php b/web/yaamp/modules/renting/all_orders_results.php new file mode 100644 index 0000000..dc9a134 --- /dev/null +++ b/web/yaamp/modules/renting/all_orders_results.php @@ -0,0 +1,95 @@ +getState('yaamp-algo'); + +$rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$defaultalgo)); +$rent = mbitcoinvaluetoa($rent); + +$renter = getrenterparam(getparam('address')); + +echo "
    "; +echo "
    All started jobs ($defaultalgo) - Current Price $rent
    "; +echo "
    "; + +//echo ""; +showTableSorter('maintable1'); +echo ""; +echo ""; +//echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$list = getdbolist('db_jobs', "ready and algo=:algo order by price desc, time", array(':algo'=>$defaultalgo)); +foreach($list as $job) +{ + $hashrate_bad = yaamp_job_rate_bad($job->id); + $hashrate = yaamp_job_rate($job->id)+$hashrate_bad; + + $title_percent = ''; + if($hashrate_bad) + { + $percent = round($hashrate_bad/$hashrate*100, 1).'%'; + $hashrate_bad = Itoa2($hashrate_bad).'h/s'; + + $title_percent = "Rejected $hashrate_bad ($percent)"; + } + + $hashrate = $hashrate? Itoa2($hashrate).'h/s': ''; + $maxhash = $job->speed? Itoa2($job->speed).'h/s': ''; + + $title = controller()->admin? "-o stratum+tcp://$job->host:$job->port -u $job->username -p $job->password": ''; + + $servername = substr($job->host, 0, 22); + $price = mbitcoinvaluetoa($job->price); + + $diff = $job->difficulty>0? round($job->difficulty, 3): ''; + $d = datetoa2($job->time); + + if($job->active) + echo ""; + else + echo ""; + + if($renter && $renter->id == $job->renterid) + echo ""; + else + echo ""; + +// echo ""; +// echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + $style_ext = ''; //$job->active? 'background-color: #dfd;': ''; + echo ""; +} + +echo "
    ServerAlgoMax PriceMax HashHash*Diff
    $job->host$d$job->algo$price$maxhash$hashrate$diff
    "; + +echo "

    +  * approximate from the last 5 minutes submitted shares
    +  ** price in mBTC/Mh/day (mBTC/Gh/day for sha256)
    +

    "; + +echo "
    "; +echo "

    "; + + + + + + + + diff --git a/web/yaamp/modules/renting/balance_results.php b/web/yaamp/modules/renting/balance_results.php new file mode 100644 index 0000000..5fcd588 --- /dev/null +++ b/web/yaamp/modules/renting/balance_results.php @@ -0,0 +1,115 @@ +"; +echo "
    Deposit $renter->address
    "; +echo "
    "; + +if(!YAAMP_RENTAL) + echo "

    Renting is temporarily disabled.

    "; + +echo ""; + +$balance = bitcoinvaluetoa($renter->balance); +$unconfirmed = bitcoinvaluetoa($renter->unconfirmed); +$spent = bitcoinvaluetoa($renter->spent); + +echo ""; +echo ""; + +if($renter->balance>=0.001) + echo ""; +else if($renter->balance>0) + echo ""; + +echo ""; + +if($unconfirmed > 0) +{ + echo ""; +} + +if(controller()->admin) +{ + echo " + "; + + if($renter->id == 7) + { +// $balance = $renter->custom_balance - $renter->custom_start; + $profit = $renter->custom_start + $renter->custom_balance - $spent; + + $start = bitcoinvaluetoa($renter->custom_start); + $balance = bitcoinvaluetoa($renter->custom_balance); + $profit = bitcoinvaluetoa($profit); + + echo ""; + echo ""; + echo ""; + } +} + +echo "
    Deposit Address$renter->address
    Balance$balance BTC(withdraw minimum 0.001)
    Unconfirmed$unconfirmed BTC"; + echo " (waiting for 1 confirmation)"; + echo "
    Spent$spent BTC
    Received$start BTC
    Unpaid$balance BTC
    Profit$profit BTC

    "; + +echo "Settings"; +echo "Logout "; + +echo "

    "; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +$list = getdbolist('db_rentertxs', "renterid=$renter->id order by time desc limit 5"); +if(count($list) == 0) return; + +echo "
    "; +echo "
    Last 5 transactions $renter->address
    "; +echo "
    "; + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($list as $tx) +{ + $d = datetoa2($tx->time); + $amount = bitcoinvaluetoa($tx->amount); + + echo ""; + + echo ""; + echo ""; + echo ""; + + if(strlen($tx->tx) > 32) + { + $tx_show = substr($tx->tx, 0, 36).'...'; + $txurl = "https://blockchain.info/tx/$tx->tx"; + echo ""; + } + else + echo ""; + + echo ""; +} + +echo "
    TimeTypeAmountTx
    $d ago$tx->type$amount$tx_show$tx->tx

    "; +echo "
    "; + +echo "

    "; + + + + + + diff --git a/web/yaamp/modules/renting/create.php b/web/yaamp/modules/renting/create.php new file mode 100644 index 0000000..8ed05bc --- /dev/null +++ b/web/yaamp/modules/renting/create.php @@ -0,0 +1,52 @@ +redirect('/renting'); + return; +} + +$captcha = new CCaptchaAction(controller(), 'captcha'); +$b = $captcha->validate($code, false); +if(!$b) +{ + controller()->redirect('/renting'); + return; +} + +// get a new btc address +$btc = getdbosql('db_coins', "symbol='BTC'"); +if(!$btc) return; + +$remote = new Bitcoin($btc->rpcuser, $btc->rpcpasswd, $btc->rpchost, $btc->rpcport); +$renter = new db_renters; + +$renter->created = time(); +$renter->updated = time(); +$renter->balance = 0; +$renter->unconfirmed = 0; +$renter->save(); + +$renter = getdbo('db_renters', $renter->id); +$renter->address = $remote->getaccountaddress(yaamp_renter_account($renter)); + +$renter->apikey = hash("sha256", $renter->address.time().rand()); +$renter->save(); + +$received1 = $remote->getbalance(yaamp_renter_account($renter), 1); +if($received1>0) +{ + $moved = $remote->move(yaamp_renter_account($renter), '', $received1); + debuglog("create new renter, moving initial $received1"); +} + +$recents = isset($_COOKIE['deposits'])? unserialize($_COOKIE['deposits']): array(); +$recents[$renter->address] = $renter->address; +setcookie('deposits', serialize($recents), time()+60*60*24*30, '/'); + +user()->setState('yaamp-deposit', $renter->address); +controller()->redirect('settings'); + + + diff --git a/web/yaamp/modules/renting/graph_job_results.php b/web/yaamp/modules/renting/graph_job_results.php new file mode 100644 index 0000000..d59da31 --- /dev/null +++ b/web/yaamp/modules/renting/graph_job_results.php @@ -0,0 +1,74 @@ +$t and jobid=:jobid order by time", array(':jobid'=>$jobid)); +$averages = array(); + +echo '[['; + +for($i = $t+$step, $j = 0; $i < time(); $i += $step) +{ + if($i != $t+$step) echo ','; + $m = 0; + + if($i + $step >= time()) + { + $m = round(yaamp_job_rate($jobid)/1000000, 3); + // debuglog("last $m"); + } + + else if(isset($stats[$j]) && $i > $stats[$j]->time) + { + $m = round($stats[$j]->hashrate/1000000, 3); + $j++; + } + + $d = date('Y-m-d H:i:s', $i); + echo "[\"$d\",$m]"; + + $averages[] = array($d, $m); +} + +echo '],['; + +$average = $averages[0][1]; +foreach($averages as $i=>$n) +{ + if($i) echo ','; + + $average = ($average*(100-$percent) + $n[1]*$percent) / 100; + $m = round($average, 3); + + echo "[\"{$n[0]}\",$m]"; +} + +echo '],['; + +for($i = $t+$step, $j = 0; $i < time(); $i += $step) +{ + if($i != $t+$step) echo ','; + $m = 0; + + if($i + $step >= time()) + { + $m = round(yaamp_user_rate_bad($jobid)/1000000, 3); + // debuglog("last $m"); + } + + else if(isset($stats[$j]) && $i > $stats[$j]->time) + { + $m = round($stats[$j]->hashrate_bad/1000000, 3); + $j++; + } + + $d = date('Y-m-d H:i:s', $i); + echo "[\"$d\",$m]"; +} + +echo ']]'; + diff --git a/web/yaamp/modules/renting/graph_price_results.php b/web/yaamp/modules/renting/graph_price_results.php new file mode 100644 index 0000000..384ab3a --- /dev/null +++ b/web/yaamp/modules/renting/graph_price_results.php @@ -0,0 +1,54 @@ +getState('yaamp-algo'); + +$step = 15*60; +$t = time() - 24*60*60; + +$stats = getdbolist('db_hashrate', "time>$t and algo=:algo order by time", array(':algo'=>$algo)); +$averages = array(); + +echo '[['; + +for($i = 0; $i < 95-count($stats); $i++) +{ + $d = date('Y-m-d H:i:s', $t); + echo "[\"$d\",0],"; + + $t += $step; + $averages[] = array($d, 0); +} + +foreach($stats as $i=>$n) +{ + $m = $n->rent; + if($m == null) $m=0; + if($i) echo ','; + + $d = date('Y-m-d H:i:s', $n->time); + echo "[\"$d\",$m]"; + + $averages[] = array($d, $m); +} + +echo '],['; + +$average = $averages[0][1]; +foreach($averages as $i=>$n) +{ + if($i) echo ','; + + $average = ($average*(100-$percent) + $n[1]*$percent) / 100; + $m = round($average, 5); + + echo "[\"{$n[0]}\",$m]"; +} + +echo ']]'; + + + + + + diff --git a/web/yaamp/modules/renting/index.php b/web/yaamp/modules/renting/index.php new file mode 100644 index 0000000..e431cc0 --- /dev/null +++ b/web/yaamp/modules/renting/index.php @@ -0,0 +1,318 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.cursor.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$this->widget('UniForm'); + +$balance = bitcoinvaluetoa($renter->balance); + +echo << + + + + +
    +
    + + + +
    +









    +
    + +
    + +
    +
    Last 24 Hours Renting ($algo)
    +

    +

    +

    + + + +









    +









    +









    +









    + + + + + + + + + + +END; + + + + + + + + + + diff --git a/web/yaamp/modules/renting/login.php b/web/yaamp/modules/renting/login.php new file mode 100644 index 0000000..1ca696c --- /dev/null +++ b/web/yaamp/modules/renting/login.php @@ -0,0 +1,239 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.cursor.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$this->widget('UniForm'); + +$address = getparam('address'); +if($address == 0) $address = ''; + +echo << +.yaamp-login-container +{ + padding: 20px; + border: 1px solid #ddd; + border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} + + + +
    + + + +
    +









    +
    + +
    + +
    +
    Last 24 Hours Renting ($algo)
    +

    +

    +

    + +
    + +









    +









    +









    +









    + + + +
    "; + + + + diff --git a/web/yaamp/modules/renting/settings.php b/web/yaamp/modules/renting/settings.php new file mode 100644 index 0000000..acf8413 --- /dev/null +++ b/web/yaamp/modules/renting/settings.php @@ -0,0 +1,156 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.cursor.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$this->widget('UniForm'); + +$renter = getrenterparam(user()->getState('yaamp-deposit')); +if(!$renter) return; + +echo << +.yaamp-login-container +{ + padding: 20px; + border: 1px solid #ddd; + border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} + + + +
    + + + + + +
    +









    +
    + +
    +
    Last 24 Hours Renting ($algo)
    +

    +

    +

    + +
    + +









    +









    +









    +









    + + + +end; + + + + + diff --git a/web/yaamp/modules/renting/status_results.php b/web/yaamp/modules/renting/status_results.php new file mode 100644 index 0000000..f46de79 --- /dev/null +++ b/web/yaamp/modules/renting/status_results.php @@ -0,0 +1,103 @@ +getState('yaamp-algo'); + +echo "
    "; +echo "
    Renting Status
    "; +echo "
    "; + +//echo ""; +showTableSorter('maintable3'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; +echo ""; + +$algos = array(); +foreach(yaamp_get_algos() as $algo) +{ + $algo_norm = yaamp_get_algo_norm($algo); + + $price = controller()->memcache->get_database_scalar("current_price-$algo", + "select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $norm = $price*$algo_norm; + $norm = take_yaamp_fee($norm, $algo); + + $algos[] = array($norm, $algo); +} + +function cmp($a, $b) +{ + return $a[0] < $b[0]; +} + +usort($algos, 'cmp'); + +foreach($algos as $item) +{ + $norm = $item[0]; + $algo = $item[1]; + + $count1 = getdbocount('db_jobs', "algo=:algo and ready and active", array(':algo'=>$algo)); + $count2 = getdbocount('db_jobs', "algo=:algo and ready", array(':algo'=>$algo)); + + $total = yaamp_pool_rate($algo); + $hashrate = yaamp_pool_rate_rentable($algo); + $hashrate_jobs = yaamp_rented_rate($algo); + + $hashrate = min($total, $hashrate); + $hashrate_jobs = min($hashrate, $hashrate_jobs); + + $available = $hashrate - $hashrate_jobs; + $percent = $hashrate_jobs && $hashrate? '('.round($hashrate_jobs/$hashrate*100, 1).'%)': ''; + + $hashrate_jobs = $hashrate_jobs>0? Itoa2($hashrate_jobs).'h/s': ''; + $available = $available>0? Itoa2($available).'h/s': ''; + $hashrate = $hashrate>0? Itoa2($hashrate).'h/s': ''; + $total = $total>0? Itoa2($total).'h/s': ''; + + $renting = controller()->memcache->get_database_scalar("current_renting-$algo", + "select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $renting = mbitcoinvaluetoa($renting); + + if($defaultalgo == $algo) + echo ""; + else + echo ""; + + echo ""; + echo ""; +// echo ""; + echo ""; + echo ""; + echo ""; + echo ""; +// echo ""; + echo ""; + echo ""; +} + +echo "
    AlgoJobsTotalFor Rent**RentedAvailablePayingCurrent Price
    $algo$count1 / $count2$total$hashrate$hashrate_jobs$percent$available$price$renting
    "; + +echo "

    +  * values in mBTC/Mh/day (mBTC/Gh/day for sha256)
    +  ** only hashpower with extranonce.subscribe or reconnect support can be rented
    +

    "; + +echo "

    "; + + + + + + diff --git a/web/yaamp/modules/renting/tx.php b/web/yaamp/modules/renting/tx.php new file mode 100644 index 0000000..6419707 --- /dev/null +++ b/web/yaamp/modules/renting/tx.php @@ -0,0 +1,74 @@ +pageTitle = "$renter->address | yaamp.com"; + +echo "
    "; +echo "
    Transactions from $renter->address
    "; +echo "
    "; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$btc = getdbosql('db_coins', "symbol='BTC'"); +if(!$btc) return; + +$remote = new Bitcoin($btc->rpcuser, $btc->rpcpasswd, $btc->rpchost, $btc->rpcport); +$ts = $remote->listtransactions(yaamp_renter_account($renter), 10); + +$res_array = array(); +foreach($ts as $val) +{ + $t = $val['time']; + if($t<$renter->created) continue; + $res_array[$t] = $val; +} + +krsort($res_array); +$total = 0; + +foreach($res_array as $transaction) +{ + if($transaction['category'] != 'receive') continue; + $d = datetoa2($transaction['time']); + + echo ""; + echo ""; + echo ""; + + if(isset($transaction['confirmations'])) + echo ""; + else + echo ""; + + echo ""; + echo ""; + + $total += $transaction['amount']; +} + +echo ""; + +echo ""; + +echo "
    TimeAmountConfirmationsTx
    $d{$transaction['amount']}{$transaction['confirmations']}"; + if(isset($transaction['txid'])) + echo "{$transaction['txid']}"; + + echo "
    total$total

    "; +echo "

    "; + + + + diff --git a/web/yaamp/modules/site/SiteController.php b/web/yaamp/modules/site/SiteController.php new file mode 100644 index 0000000..b6a336b --- /dev/null +++ b/web/yaamp/modules/site/SiteController.php @@ -0,0 +1,772 @@ +setState('yaamp_admin', true); + $this->redirect("/site/common"); + } + + ///////////////////////////////////////////////// + + public function actionCreate() + { + if(!$this->admin) return; + + $coin = new db_coins; + $coin->txmessage = true; + $coin->created = time(); + $coin->index_avg = 1; + $coin->difficulty = 1; + $coin->installed = 1; + $coin->visible = 1; + + // $coin->deposit_minimum = 1; + $coin->lastblock = ''; + + if(isset($_POST['db_coins'])) + { + $coin->attributes = $_POST['db_coins']; + if($coin->save()) + $this->redirect(array('admin')); + } + + $this->render('coin_form', array('update'=>false, 'coin'=>$coin)); + } + + public function actionUpdate() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', getiparam('id')); + $txfee = $coin->txfee; + + if(isset($_POST['db_coins'])) + { + $coin->attributes = $_POST['db_coins']; + if($coin->save()) + { + if($txfee != $coin->txfee) + { + $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + $remote->settxfee($coin->txfee); + } + + // $this->redirect(array('admin')); + $this->goback(); + } + } + + $this->render('coin_form', array('update'=>true, 'coin'=>$coin)); + } + + ///////////////////////////////////////////////// + + public function actionIndex() + { + if(isset($_GET['address'])) + $this->render('wallet'); + else + $this->render('index'); + } + + public function actionApi() + { + $this->render('api'); + } + + public function actionDiff() + { + $this->render('diff'); + } + + public function actionMultialgo() + { + $this->render('multialgo'); + } + + public function actionMining() + { + $this->render('mining'); + } + + public function actionMiners() + { + $this->render('miners'); + } + + ///////////////////////////////// + + public function actionCurrent_results() + { + $this->renderPartial('results/current_results'); + } + + public function actionHistory_results() + { + $this->renderPartial('results/history_results'); + } + + public function actionMining_results() + { + $this->renderPartial('results/mining_results'); + } + + public function actionMiners_results() + { + $this->renderPartial('results/miners_results'); + } + + public function actionWallet_results() + { + $this->renderPartial('results/wallet_results'); + } + + public function actionWallet_miners_results() + { + $this->renderPartial('results/wallet_miners_results'); + } + + public function actionWallet_graphs_results() + { + $this->renderPartial('results/wallet_graphs_results'); + } + + public function actionGraph_earnings_results() + { + $this->renderPartial('results/graph_earnings_results'); + } + + public function actionFound_results() + { + $this->renderPartial('results/found_results'); + } + + public function actionUser_earning_results() + { + $this->renderPartial('results/user_earning_results'); + } + + public function actionGraph_hashrate_results() + { + $this->renderPartial('results/graph_hashrate_results'); + } + + public function actionGraph_user_results() + { + $this->renderPartial('results/graph_user_results'); + } + + public function actionGraph_price_results() + { + $this->renderPartial('results/graph_price_results'); + } + + public function actionGraph_assets_results() + { + $this->renderPartial('results/graph_assets_results'); + } + + public function actionGraph_negative_results() + { + $this->renderPartial('results/graph_negative_results'); + } + + public function actionGraph_profit_results() + { + $this->renderPartial('results/graph_profit_results'); + } + + public function actionTitle_results() + { + $user = getuserparam(getparam('address')); + if($user) + { + $balance = bitcoinvaluetoa($user->balance); + $coin = getdbo('db_coins', $user->coinid); + + if($coin) + echo "$balance $coin->symbol - yaamp.com"; + else + echo "$balance - yaamp.com"; + } + else + echo "yaamp.com"; + } + + ///////////////////////////////////////////////// + + public function actionAbout() + { + $this->render('about'); + } + + public function actionTerms() + { + $this->render('terms'); + } + + ///////////////////////////////////////////////// + + public function actionAdmin() + { + if(!$this->admin) return; + $this->render('admin'); + } + + public function actionAdmin_results() + { + if(!$this->admin) return; + $this->renderPartial('admin_results'); + } + + ///////////////////////////////////////////////// + + public function actionConnections() + { + if(!$this->admin) return; + $this->render('connections'); + } + + public function actionConnections_results() + { + if(!$this->admin) return; + $this->renderPartial('connections_results'); + } + + ///////////////////////////////////////////////// + + public function actionBlock() + { + $this->render('block'); + } + + public function actionBlock_results() + { + $this->renderPartial('block_results'); + } + + ///////////////////////////////////////////////// + + public function actionEarning() + { + if(!$this->admin) return; + $this->render('earning'); + } + + public function actionEarning_results() + { + if(!$this->admin) return; + $this->renderPartial('earning_results'); + } + + ///////////////////////////////////////////////// + + public function actionPayments() + { + if(!$this->admin) return; + $this->render('payments'); + } + + public function actionPayments_results() + { + if(!$this->admin) return; + $this->renderPartial('payments_results'); + } + + ///////////////////////////////////////////////// + + public function actionUser() + { + if(!$this->admin) return; + $this->render('user'); + } + + public function actionUser_results() + { + if(!$this->admin) return; + $this->renderPartial('user_results'); + } + + ///////////////////////////////////////////////// + + public function actionWorker() + { + if(!$this->admin) return; + $this->render('worker'); + } + + public function actionWorker_results() + { + if(!$this->admin) return; + $this->renderPartial('worker_results'); + } + + ///////////////////////////////////////////////// + + public function actionVersion() + { + if(!$this->admin) return; + $this->render('version'); + } + + public function actionVersion_results() + { + if(!$this->admin) return; + $this->renderPartial('version_results'); + } + + ///////////////////////////////////////////////// + + public function actionCommon() + { + if(!$this->admin) return; + $this->render('common'); + } + + public function actionCommon_results() + { + if(!$this->admin) return; + $this->renderPartial('common_results'); + } + + ///////////////////////////////////////////////// + + public function actionExchange() + { + if(!$this->admin) return; + $this->render('exchange'); + } + + public function actionExchange_results() + { + if(!$this->admin) return; + $this->renderPartial('exchange_results'); + } + + ///////////////////////////////////////////////// + + public function actionCoin() + { + if(!$this->admin) return; + $this->render('coin'); + } + + public function actionCoin_results() + { + if(!$this->admin) return; + $this->renderPartial('coin_results'); + } + + public function actionMemcached() + { + if(!$this->admin) return; + $this->render('memcached'); + } + + public function actionMonsters() + { + if(!$this->admin) return; + $this->render('monsters'); + } + + public function actionEmptyMarkets() + { + if(!$this->admin) return; + $this->render('emptymarkets'); + } + + ////////////////////////////////////////////////////////////////////////////////////// + + public function actionTx() + { + $this->renderPartial('tx'); + } + + ////////////////////////////////////////////////////////////////////////////////////// + + public function actionResetBlockchain() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + $coin->action = 3; + $coin->save(); + + $this->redirect("/site/coin?id=$coin->id"); + } + + ////////////////////////////////////////////////////////////////////////////////////// + + public function actionRestartCoin() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->action = 4; + $coin->enable = false; + $coin->auto_ready = false; + $coin->installed = true; + $coin->connections = 0; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + public function actionStartCoin() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->action = 1; + $coin->enable = true; + $coin->auto_ready = false; + $coin->installed = true; + $coin->connections = 0; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + public function actionStopCoin() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->action = 2; + $coin->enable = false; + $coin->auto_ready = false; + $coin->connections = 0; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + public function actionMakeConfigfile() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->action = 5; + $coin->installed = true; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + ////////////////////////////////////////////////////////////////////////////////////// + + public function actionSetauto() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->auto_ready = true; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + public function actionUnsetauto() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', $_GET['id']); + + $coin->auto_ready = false; + $coin->save(); + + $this->redirect('/site/admin'); + // $this->goback(); + } + + public function actionSellBalance() + { + if(!$this->admin) return; + $coin = getdbo('db_coins', getiparam('id')); + $amount = getparam('amount'); + + $res = $this->doSellBalance($coin, $amount); + + if(!$res) + $this->redirect('/site/admin'); + else + $this->redirect('/site/exchange'); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + public function actionBlockuser() + { + if(!$this->admin) return; + + $wallet = getparam('wallet'); + $user = getuserparam($wallet); + + $user->is_locked = true; + $user->save(); + + $this->redirect('/site/monsters'); + } + + public function actionUnblockuser() + { + if(!$this->admin) return; + + $wallet = getparam('wallet'); + $user = getuserparam($wallet); + + $user->is_locked = false; + $user->save(); + + $this->redirect('/site/monsters'); + } + + public function actionPayuserscoin() + { + if(!$this->admin) return; + + $coin = getdbo('db_coins', getiparam('id')); + if(!$coin) + { + debuglog("coin not found"); + return; + } + + BackendCoinPayments($coin); + $this->goback(); + } + + //// + + public function actionDeleteEarnings() + { + if(!$this->admin) return; + + $coin = getdbo('db_coins', getiparam('id')); + if(!$coin) + { + debuglog("coin not found"); + return; + } + +// $list = getdbolist('db_earnings', "coinid=$coin->id and not cleared"); +// foreach($list as $earning) $earning->delete(); + + dborun("delete from earnings where coinid=$coin->id"); + $this->redirect("/site/admin"); + } + + public function actionDeleteEarning() + { + if(!$this->admin) return; + $earning = getdbo('db_earnings', $_GET['id']); + $earning->delete(); + + $this->redirect('/site/earning'); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + public function actionDeleteExchange() + { + $exchange = getdbo('db_exchange', getiparam('id')); + $unspent = $exchange->quantity; + + $exchange->status = 'deleted'; + $exchange->price = 0; + $exchange->receive_time = time(); + $exchange->save(); + +// $earnings = getdbolist('db_earnings', "coinid=$exchange->coinid and not cleared order by create_time"); +// foreach($earnings as $earning) +// { +// $unspent -= $earning->amount; +// $earning->delete(); + +// if($unspent <= 0) break; +// } + + $this->redirect('/site/exchange'); + } + + public function actionClearMarket() + { + $id = getiparam('id'); + $market = getdbo('db_markets', $id); + + if($market) + { + $market->lastsent = null; + $market->save(); + } + + $this->redirect('/site/common'); + } + + //////////////////////////////////////////////////////////////////////////////////////// + + public function actionAlgo() + { + $algo = substr(getparam('algo'), 0, 32); + $a = getdbosql('db_algos', "name=:name", array(':name'=>$algo)); + + if($a) + user()->setState('yaamp-algo', $a->name); + else + user()->setState('yaamp-algo', 'all'); + + $this->goback(); + } + + public function actionGomining() + { + user()->setState('yaamp-algo', getparam('algo')); + $this->redirect("/site/mining"); + } + + public function actionUpdatePrice() + { + if(!$this->admin) return; + BackendPricesUpdate(); + $this->goback(); + } + + public function actionUninstallCoin() + { + if(!$this->admin) return; + + $coin = getdbo('db_coins', getiparam('id')); + if($coin) + { + // dborun("delete from blocks where coin_id=$coin->id"); + dborun("delete from exchange where coinid=$coin->id"); + dborun("delete from earnings where coinid=$coin->id"); + // dborun("delete from markets where coinid=$coin->id"); + dborun("delete from orders where coinid=$coin->id"); + dborun("delete from shares where coinid=$coin->id"); + + $coin->enable = false; + $coin->installed = false; + $coin->auto_ready = false; + $coin->master_wallet = null; + $coin->mint = 0; + $coin->balance = 0; + $coin->save(); + } + + $this->redirect("/site/admin"); + } + + public function actionBanUser() + { + if(!$this->admin) return; + + $user = getdbo('db_accounts', getiparam('id')); + if($user) + { + $user->is_locked = true;; + $user->balance = 0; + $user->save(); + } + + $this->goback(); + } + + public function actionOptimize() + { + BackendOptimizeTables(); + $this->goback(); + } + + public function actionRunExchange() + { + $id = getiparam('id'); + $balance = getdbo('db_balances', $id); + + if($balance) switch($balance->name) + { + case 'cryptsy': + doCryptsyTrading(true); + updateCryptsyMarkets(); + + break; + + case 'bittrex': + doBittrexTrading(true); + updateBittrexMarkets(); + + break; + + case 'c-cex': + doCCexTrading(true); + updateCCexMarkets(); + + break; + + case 'yobit': + doYobitTrading(true); + updateYobitMarkets(); + + break; + + case 'bleutrade': + doBleutradeTrading(true); + updateBleutradeMarkets(); + + break; + + case 'poloniex': + doPoloniexTrading(true); + updatePoloniexMarkets(); + + break; + } + + debuglog("runexchange done"); + $this->redirect("/site/common"); + } + + public function actionEval() + { + if(!$this->admin) return; + +// $param = getparam('param'); +// if($param) eval($param); +// else $param = ''; + +// $this->render('eval', array('param'=>$param)); + } + + public function actionMainbtc() + { + debuglog(__METHOD__); + setcookie('mainbtc', '1', time()+60*60*24, '/'); + } + + public function actionTest() + { + // if(!$this->admin) return; + debuglog("action test"); + + $ticker = jubi_api_query('ticker', "?coin=sak"); + debuglog($ticker); + + debuglog("action test end"); + } + +} + + + + + diff --git a/web/yaamp/modules/site/about.php b/web/yaamp/modules/site/about.php new file mode 100644 index 0000000..e04fced --- /dev/null +++ b/web/yaamp/modules/site/about.php @@ -0,0 +1,59 @@ + + +

    A few words to present the site and myself

    + +
    + +

    Although i've been exposed by friends who are mining bitcoin or some other dogecoin +for a little while already, i'm pretty new to the cryptocoin mining universe. I do like +the concept of cryptocurrency for many reasons. One of them being certainly to lessen the +monetary policy control from the few hands. Cryptocurrencies seem well suited to something +that looks like a libertarianism political philosophy. That's probably going to be a very +slow and long term shift, but i guess that's a step toward freedom and democracy. Numbers +would normally eventually outweigh the few. Another reason is because it's fun.

    + +

    I must say that it's the maxcoin launch that really got my attention. Because mining +early is good and because i feel Max Keiser is a relevant voice for cryptocurrencies. I +got myself a decent GPU and started mining maxcoin. I've got a few by mining at 1gh.com +for a week. It did the job very well. Then i've been aware of a possible 51% attack on +cryptocurrency. I really don't think 1gh.com would mess with anything, but still, that's +an issue. As there are already many other mining pools, it looks like anonymous mining is +popular. Especially at maxcoin launch when they all got ddosed. I went from one to +another before i got something working on 1gh.com. Then i agreed that anonymous mining +is great and my password paper notebook had enough.

    + +

    I understand the only available implementation out there is not anonymous. Everyone has +the same stratum-mining + MPOS implementation. Only 1gh.com and dwarfmining seem to have +their own original modified implementation or i didn't find correctly. So i saw there an +opportunity for another anonymous mining pool. For a whole day, i tried to refrain myself +from this crazy idea of making another mining pool. I'm some kind of compulsive coder. I +do it for a living and i do it for fun. And that's the kind of project i like.

    + +

    Crazy idea because there aren't much chances for success. I mean nobody is going to +come to an empty mining site. The opportunity window is very early at the coin launch +when low hashpower can still find blocks. Even though it was a few days late to start +such a project for maxcoin, the next day, that was too strong, i had to do it. Else i +was stuck from doing my other projects. In any way, if i'm going to be involved in the +cryptocurrency world, it's going to be also as a programmer or at hacking here and there +sooner or later. That was also an opportunity to jump in some python code.

    + +

    This server is online for at least one year. I do have other servers for other projects +too. They are so cheap. So i'll continue to plug in my miner (and play a little less world +of warcraft) and look forward to acquire more hashpower. But another thing i get from this +last weekend journey is some knowledge of the technology so i can think about contributing +to the programming effort whenever i can and be ready whenever there's another interesting +coin launch.

    + +

    Now i can go back to work,

    + +

    Long life to maxcoin,

    + +

    neozon

    + +

    19/02/2014

    + +
    + +









    +









    + diff --git a/web/yaamp/modules/site/admin.php b/web/yaamp/modules/site/admin.php new file mode 100644 index 0000000..3eebd69 --- /dev/null +++ b/web/yaamp/modules/site/admin.php @@ -0,0 +1,90 @@ +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  +Big Miners  +EmptyMarket  + +
    +Select Server: +  +
    + +
    +









    +









    +









    +









    +









    +
    + +
    CREATE COIN + + + +









    + + + +end; + + diff --git a/web/yaamp/modules/site/admin_results.php b/web/yaamp/modules/site/admin_results.php new file mode 100644 index 0000000..b26cee6 --- /dev/null +++ b/web/yaamp/modules/site/admin_results.php @@ -0,0 +1,171 @@ +"; +//showTableSorter('maintable'); +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; + +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo ""; +echo ""; + +$current_algo = ''; + +$server = getparam('server'); +if(!empty($server)) +{ + $coins = getdbolist('db_coins', "(installed or enable) and rpchost=:server order by algo, index_avg desc", + array(':server'=>$server)); +} +else + $coins = getdbolist('db_coins', "installed or enable order by algo, index_avg desc"); + +$total = count($coins); +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($coins as $coin) +{ + if($coin->algo != $current_algo) + { + $current_algo = $coin->algo; + echo ""; + } + else + echo ""; + + $lowsymbol = strtolower($coin->symbol); + echo ""; + + $algo_color = getAlgoColors($coin->algo); + echo ""; + $version = substr($coin->version, 0, 25); + + echo ""; + + echo ""; + + $difficulty = Itoa2($coin->difficulty, 3); + + if(!empty($coin->errors)) + echo ""; + else + echo ""; + +// $network_ttf = $coin->network_ttf? sectoa($coin->network_ttf): ''; +// $actual_ttf = $coin->actual_ttf? sectoa($coin->actual_ttf): ''; +// $pool_ttf = $coin->pool_ttf? sectoa($coin->pool_ttf): ''; + $btcmhd = yaamp_profitability($coin); + $btcmhd = mbitcoinvaluetoa($btcmhd); + + $h = $coin->block_height-100; + $ss1 = dboscalar("select count(*) from blocks where coin_id=$coin->id and height>=$h and category!='orphan'"); + $ss2 = dboscalar("select count(*) from blocks where coin_id=$coin->id and height>=$h and category='orphan'"); + + $percent_pool1 = $ss1? $ss1.'%': ''; + $percent_pool2 = $ss2? $ss2.'%': ''; + +// echo ""; +// echo ""; + + if($ss1 > 50) + echo ""; + + $owed = dboscalar("select sum(balance) from accounts where coinid=$coin->id"); + $owed_btc = bitcoinvaluetoa($owed*$coin->price); + $owed = bitcoinvaluetoa($owed); + + if($coin->balance+$coin->mint < $owed) + echo ""; + else + echo ""; + + $btc = bitcoinvaluetoa($coin->balance*$coin->price); + echo ""; + + $btc = bitcoinvaluetoa($coin->mint*$coin->price); + echo ""; + + $price = bitcoinvaluetoa($coin->price); + $price2 = bitcoinvaluetoa($coin->price2); +// $marketcount = getdbocount('db_markets', "coinid=$coin->id"); + + $marketname = ''; + $bestmarket = getBestMarket($coin); + if($bestmarket) $marketname = $bestmarket->name; + + if($coin->dontsell) + echo ""; + else + echo ""; + + echo ""; + + echo ""; +} + +echo ""; +echo "
    NameServerDiff/HeightProfitOwed/BTCBalance/BTCMint/BTCPriceWin/Market
    $total Coins
    "; + + if($coin->enable) + { + echo "u"; + if($coin->auto_ready) echo " a"; + else echo " d"; + + echo '
    '; + + if($coin->visible) echo "v"; + else echo ' '; + + if($coin->auxpow) echo " x"; + + if($coin->block_height < $coin->target_height) + { + $percent = round($coin->block_height*100/$coin->target_height, 2); + echo "
    $percent%"; + } + } + + echo "
    $coin->name ($coin->symbol) +
    $version ($coin->symbol2)
    $coin->rpchost:$coin->rpcport"; + if($coin->connections) echo " ($coin->connections)"; + echo "
    $coin->rpcencoding   ($coin->algo)  
    $difficulty
    $coin->block_height
    $difficulty
    $coin->block_height
    $network_ttf
    $actual_ttf
    $pool_ttf
    $btcmhd
    $percent_pool1"; + else + echo "
    $btcmhd
    $percent_pool1"; + + echo " $percent_pool2
    $owed
    $owed_btc
    $owed
    $owed_btc
    $coin->balance
    $btc
    $coin->mint
    $btc
    $price
    $price2
    $price
    $price2
    $coin->reward
    $marketname
    "; + +////////////////////////////////////////// + +echo "
    "; + + + + + + + + + + + + + diff --git a/web/yaamp/modules/site/api.php b/web/yaamp/modules/site/api.php new file mode 100644 index 0000000..af65287 --- /dev/null +++ b/web/yaamp/modules/site/api.php @@ -0,0 +1,136 @@ +
    + +
    +
    YAAMP API
    +
    + +

    Simple REST API.

    + +

    Pool Status

    + +request: +

    + http://yaamp.com/api/status

    + +result: +
    +{
    +	"x11":
    +	{
    +		"coins": 10, 
    +		"fees": 1, 
    +		"hashrate": 269473938, 
    +		"estimate_current": 0.00053653, 
    +		"estimate_last24h": 0.00036408, 
    +		"actual_last24h": 0.00035620
    +	},
    +		
    +	...
    +}
    +
    + + +

    Wallet Status

    + +request: +

    + http://yaamp.com/api/wallet?address=BITCOIN_WALLET

    + +result: +
    +{
    +	"unsold": 0.00050362, 
    +	"balance": 0.00000000, 
    +	"unpaid": 0.00050362, 
    +	"paid": 0.00000000, 
    +	"total": 0.00050362
    +}
    +
    + +

    Rental Status

    + +request: +

    + http://yaamp.com/api/rental?key=API_KEY

    + +result: +
    +{
    +	"balance": 0.00000000, 
    +	"unconfirmed": 0.00000000, 
    +	"jobs":
    +	[
    +		{
    +			"jobid": "19",
    +			"algo": "x11",
    +			"price": "1",
    +			"hashrate": "1000000",
    +			"server": "stratum.server.com",
    +			"port": "3333",
    +			"username": "1A5pAdfWLUFXoqcUb6N9Fre2EApr5QLNdG",
    +			"password": "xx",
    +			"started": "1",
    +			"active": "1",
    +			"accepted": "586406.2014805333",
    +			"rejected": "",
    +			"diff": "0.04"
    +		},
    +         		
    + 		...
    + 		
    +	]
    +}
    +
    + +

    Rental Price

    + +

    Set the rental price of a job.

    + +request: +

    + http://yaamp.com/api/rental_price?key=API_KEY&jobid=xx&price=xx

    + +
    + +

    Rental Hashrate

    + +

    Set the rental max hashrate of a job.

    + +request: +

    + http://yaamp.com/api/rental_hashrate?key=API_KEY&jobid=xx&hashrate=xx

    + +
    + +

    Start Rental Job

    + +request: +

    + http://yaamp.com/api/rental_start?key=API_KEY&jobid=xx

    + + + +

    Stop Rental Job

    + +request: +

    + http://yaamp.com/api/rental_stop?key=API_KEY&jobid=xx

    + + + + +

    + + + +









    +









    +









    +









    + + + + diff --git a/web/yaamp/modules/site/backup.php b/web/yaamp/modules/site/backup.php new file mode 100644 index 0000000..30ffcfa --- /dev/null +++ b/web/yaamp/modules/site/backup.php @@ -0,0 +1,33 @@ + +
    +
    FAQ:
    +
    + +
      + +
    • Q. Other anonymous mining pools require the -p switch with a fake password, do I need +that on my miner command line?
    • + +
    • A. No, it's not necessary but it works with it too. It's just ignored.
    • + +
    • Q. What happens if I leave before the next block is found?
    • +
    • A. Your shares are kept until the next block is found and you will receive payment +for them.
    • + +
    + +
    +
    +
    + + + + +

    + + + + + + + diff --git a/web/yaamp/modules/site/block.php b/web/yaamp/modules/site/block.php new file mode 100644 index 0000000..481db99 --- /dev/null +++ b/web/yaamp/modules/site/block.php @@ -0,0 +1,46 @@ + + +









    +









    +









    +









    + + + +end; + + diff --git a/web/yaamp/modules/site/block_results.php b/web/yaamp/modules/site/block_results.php new file mode 100644 index 0000000..1f53707 --- /dev/null +++ b/web/yaamp/modules/site/block_results.php @@ -0,0 +1,85 @@ +$id)); +else + $db_blocks = getdbolist('db_blocks', "1 order by time desc limit 250"); + +echo ""; +//showTableSorter('maintable'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($db_blocks as $db_block) +{ + if(!$db_block->coin_id) continue; + + $coin = getdbo('db_coins', $db_block->coin_id); + if(!$coin) continue; + +// $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + +// $blockext = $remote->getblock($db_block->blockhash); +// $tx = $remote->gettransaction($blockext['tx'][0]); + +// $db_block->category = $tx['details'][0]['category']; + + if($db_block->category == 'immature') + echo ""; + else + echo ""; + + echo ""; + echo ""; + +// $db_block->confirmations = $blockext['confirmations']; +// $db_block->save(); + + $d = datetoa2($db_block->time); + echo ""; + + echo ""; + echo ""; + + echo ""; + + echo ""; + echo ""; + + echo ""; + echo ""; +} + +echo "
    NameTimeHeightAmountStatusDifficultyFound DiffBlockhash
    $coin->name ($coin->symbol)$d ago$db_block->height$db_block->amount"; + + if($db_block->category == 'orphan') + echo "Orphan"; + + else if($db_block->category == 'immature') + echo "Immature ($db_block->confirmations)"; + + else if($db_block->category == 'generate') + echo 'Confirmed'; + + echo "$db_block->difficulty$db_block->difficulty_user$db_block->blockhash
    "; + + + + + + + + + diff --git a/web/yaamp/modules/site/coin.php b/web/yaamp/modules/site/coin.php new file mode 100644 index 0000000..71c4376 --- /dev/null +++ b/web/yaamp/modules/site/coin.php @@ -0,0 +1,145 @@ + +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  +
    + +rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); +$info = $remote->getinfo(); + +echo "
    COIN PROPERTIES"; +echo " || EXTRA"; + +if($info) + echo " || EXPLORER"; + +if(!$info && $coin->enable) + echo "
    STOP COIND"; + +if($coin->auto_ready) + echo "
    UNSET AUTO"; +else + echo "
    SET AUTO"; + +echo "
    "; + +if(!empty($coin->link_bitcointalk)) + echo "forum "; + +if(!empty($coin->link_github)) + echo "git "; + +echo "google "; + +echo "
    "; + +echo "
    MAKE CONFIG & START"; + +if($info) +{ + echo "
    RESTART COIND"; + echo "
    STOP COIND"; + +// if(isset($info['balance']) && $info['balance'] && !empty($coin->deposit_address)) +// echo "
    SEND BALANCE TO - $coin->deposit_address"; +} +else +{ + echo "
    START COIND"; + echo "

    RESET BLOCKCHAIN"; + + if($coin->installed) + echo "
    UNINSTALL COIN
    "; +} + +echo "
    CLEAR EARNINGS"; +echo "
    DELETE EARNINGS"; +echo "
    DO PAYMENTS"; +//echo "
    CHECK FOR NEW BLOCKS"; + +echo <<








    +









    +









    +









    + + + + + +END; + + + diff --git a/web/yaamp/modules/site/coin_form.php b/web/yaamp/modules/site/coin_form.php new file mode 100644 index 0000000..5c1ef54 --- /dev/null +++ b/web/yaamp/modules/site/coin_form.php @@ -0,0 +1,229 @@ + +Summary  +Coins  +Users  +Workers  +Version  +Earnings  +Payments  + +id'>$coin->name
    "; + +//include "current.php"; +$this->widget('UniForm'); + +echo CUFHtml::beginForm(); +echo CUFHtml::errorSummary($coin); +echo CUFHtml::openTag('fieldset', array('class'=>'inlineLabels')); + +InitMenuTabs('#tabs'); + +echo "

    "; + +echo "
    "; + +echo CUFHtml::openActiveCtrlHolder($coin, 'name'); +echo CUFHtml::activeLabelEx($coin, 'name'); +echo CUFHtml::activeTextField($coin, 'name', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'symbol'); +echo CUFHtml::activeLabelEx($coin, 'symbol'); +echo CUFHtml::activeTextField($coin, 'symbol', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'symbol2'); +echo CUFHtml::activeLabelEx($coin, 'symbol2'); +echo CUFHtml::activeTextField($coin, 'symbol2', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'algo'); +echo CUFHtml::activeLabelEx($coin, 'algo'); +echo CUFHtml::activeTextField($coin, 'algo', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'image'); +echo CUFHtml::activeLabelEx($coin, 'image'); +echo CUFHtml::activeTextField($coin, 'image', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'reward'); +echo CUFHtml::activeLabelEx($coin, 'reward'); +echo CUFHtml::activeTextField($coin, 'reward', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'reward_mul'); +echo CUFHtml::activeLabelEx($coin, 'reward_mul'); +echo CUFHtml::activeTextField($coin, 'reward_mul', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'block_height'); +echo CUFHtml::activeLabelEx($coin, 'block_height'); +echo CUFHtml::activeTextField($coin, 'block_height', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'target_height'); +echo CUFHtml::activeLabelEx($coin, 'target_height'); +echo CUFHtml::activeTextField($coin, 'target_height', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo "
    "; + +////////////////////////////////////////////////////////////////////////////////////////// + +echo "
    "; + +echo CUFHtml::openActiveCtrlHolder($coin, 'enable'); +echo CUFHtml::activeLabelEx($coin, 'enable'); +echo CUFHtml::activeCheckBox($coin, 'enable'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'auto_ready'); +echo CUFHtml::activeLabelEx($coin, 'auto_ready'); +echo CUFHtml::activeCheckBox($coin, 'auto_ready'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'visible'); +echo CUFHtml::activeLabelEx($coin, 'visible'); +echo CUFHtml::activeCheckBox($coin, 'visible'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'installed'); +echo CUFHtml::activeLabelEx($coin, 'installed'); +echo CUFHtml::activeCheckBox($coin, 'installed'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'auxpow'); +echo CUFHtml::activeLabelEx($coin, 'auxpow'); +echo CUFHtml::activeCheckBox($coin, 'auxpow'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +// echo CUFHtml::openActiveCtrlHolder($coin, 'index_avg'); +// echo CUFHtml::activeLabelEx($coin, 'index_avg'); +// echo CUFHtml::activeTextField($coin, 'index_avg', array('maxlength'=>200)); +// echo "

    .

    "; +// echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'dontsell'); +echo CUFHtml::activeLabelEx($coin, 'dontsell'); +echo CUFHtml::activeCheckBox($coin, 'dontsell'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'sellonbid'); +echo CUFHtml::activeLabelEx($coin, 'sellonbid'); +echo CUFHtml::activeCheckBox($coin, 'sellonbid'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'txfee'); +echo CUFHtml::activeLabelEx($coin, 'txfee'); +echo CUFHtml::activeTextField($coin, 'txfee', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'master_wallet'); +echo CUFHtml::activeLabelEx($coin, 'master_wallet'); +echo CUFHtml::activeTextField($coin, 'master_wallet', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'errors'); +echo CUFHtml::activeLabelEx($coin, 'errors'); +echo CUFHtml::activeTextField($coin, 'errors', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo "
    "; + +////////////////////////////////////////////////////////////////////////////////////////// + +echo "
    "; + +// echo CUFHtml::openActiveCtrlHolder($coin, 'program'); +// echo CUFHtml::activeLabelEx($coin, 'program'); +// echo CUFHtml::activeTextField($coin, 'program', array('maxlength'=>200)); +// echo "

    .

    "; +// echo CUFHtml::closeCtrlHolder(); + +// echo CUFHtml::openActiveCtrlHolder($coin, 'conf_folder'); +// echo CUFHtml::activeLabelEx($coin, 'conf_folder'); +// echo CUFHtml::activeTextField($coin, 'conf_folder', array('maxlength'=>200)); +// echo "

    .

    "; +// echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'rpchost'); +echo CUFHtml::activeLabelEx($coin, 'rpchost'); +echo CUFHtml::activeTextField($coin, 'rpchost', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +if(empty($coin->rpcport)) + $coin->rpcport = $coin->id*10; + +echo CUFHtml::openActiveCtrlHolder($coin, 'rpcport'); +echo CUFHtml::activeLabelEx($coin, 'rpcport'); +echo CUFHtml::activeTextField($coin, 'rpcport', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +if(empty($coin->rpcuser)) + $coin->rpcuser = 'yaamprpc'; + +echo CUFHtml::openActiveCtrlHolder($coin, 'rpcuser'); +echo CUFHtml::activeLabelEx($coin, 'rpcuser'); +echo CUFHtml::activeTextField($coin, 'rpcuser', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +if(empty($coin->rpcpasswd)) + $coin->rpcpasswd = 'e7ec005fhdsj3k6po6afbb84545eb24'; + +echo CUFHtml::openActiveCtrlHolder($coin, 'rpcpasswd'); +echo CUFHtml::activeLabelEx($coin, 'rpcpasswd'); +echo CUFHtml::activeTextField($coin, 'rpcpasswd', array('maxlength'=>200)); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'hassubmitblock'); +echo CUFHtml::activeLabelEx($coin, 'hassubmitblock'); +echo CUFHtml::activeCheckBox($coin, 'hassubmitblock'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo CUFHtml::openActiveCtrlHolder($coin, 'txmessage'); +echo CUFHtml::activeLabelEx($coin, 'txmessage'); +echo CUFHtml::activeCheckBox($coin, 'txmessage'); +echo "

    .

    "; +echo CUFHtml::closeCtrlHolder(); + +echo "
    "; + +echo "
    "; + +echo CUFHtml::closeTag('fieldset'); +showSubmitButton($update? 'Save': 'Create'); +echo CUFHtml::endForm(); + + + diff --git a/web/yaamp/modules/site/coin_results.php b/web/yaamp/modules/site/coin_results.php new file mode 100644 index 0000000..b8ab811 --- /dev/null +++ b/web/yaamp/modules/site/coin_results.php @@ -0,0 +1,242 @@ +rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport); + +$reserved1 = dboscalar("select sum(balance) from accounts where coinid=$coin->id"); +$reserved2 = dboscalar("select sum(amount*price) from earnings + where status!=2 and userid in (select id from accounts where coinid=$coin->id)"); + +$reserved = ($reserved1 + $reserved2) * 2; + +$owed = dboscalar("select sum(amount) from earnings where status!=2 and coinid=$coin->id"); +$owed_btc = $owed? bitcoinvaluetoa($owed*$coin->price): ''; +$owed = $owed? altcoinvaluetoa($owed): ''; + +echo "cleared $reserved1, earnings $reserved2, reserved $reserved, balance $coin->balance, owed $owed, owned btc $owed_btc

    "; + +////////////////////////////////////////////////////////////////////////////////////// + +$list = getdbolist('db_markets', "coinid=$coin->id order by price desc"); + +echo ""; +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$bestmarket = getBestMarket($coin); +foreach($list as $market) +{ + $price = bitcoinvaluetoa($market->price); + $price2 = bitcoinvaluetoa($market->price2); + $lowsymbol = strtolower($coin->symbol); + + if($market->name == 'cryptsy') + $marketurl = "https://www.cryptsy.com/markets/view/$market->marketid"; + + else if($market->name == 'bittrex') + $marketurl = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($market->name == 'mintpal') + $marketurl = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($market->name == 'poloniex') + $marketurl = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($market->name == 'bleutrade') + $marketurl = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else if($market->name == 'c-cex') + $marketurl = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($market->name == 'yobit') + $marketurl = "https://yobit.net/en/trade/$coin->symbol/BTC"; + + else if($market->name == 'jubi') + $marketurl = "http://jubi.com/coin/$lowsymbol"; + + if($bestmarket && $market->id == $bestmarket->id) + echo ""; + else + echo ""; + + echo ""; + + echo ""; + echo ""; + + $sent = datetoa2($market->lastsent); + $traded = datetoa2($market->lasttraded); + $late = $sent > $traded? 'late': ''; + + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; +} + +echo "
    NamePricePrice2SentTradedLateDepositMessage
    $market->name$price$price2$sent ago$traded ago$late$market->deposit_address "; + + echo "edit "; + echo "sell "; + echo "del$market->message

    "; + +////////////////////////////////////////////////////////////////////////////////////// + +echo ""; +//showTableSorter('maintable'); +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo ""; +echo ""; +echo ""; + +echo ""; +echo ""; + +echo ""; +echo ""; + +if($coin->enable) + echo ""; +else + echo ""; + +echo ""; +echo ""; + +$info = $remote->getinfo(); +if(!$info) +{ + echo "ERROR $remote->error

    "; + +// echo ""; + return; +} + +$errors = isset($info['errors'])? $info['errors']: ''; +$balance = isset($info['balance'])? $info['balance']: ''; +$txfee = isset($info['paytxfee'])? $info['paytxfee']: ''; +$connections = isset($info['connections'])? $info['connections']: ''; +$blocks = isset($info['blocks'])? $info['blocks']: ''; + +echo ""; +if(!empty($errors)) + echo ""; +else + echo ""; + +echo ""; + +$btc = $balance*$coin->price; +echo ""; +echo ""; + +echo ""; +echo ""; + +if($coin->difficulty) + $index = round($coin->reward * $coin->price / $coin->difficulty * 10000, 3); + +echo ""; +echo ""; + +echo "
    NameSymbolDifficultyBlocksBalanceBTCConnsPriceRewardIndex *
    [ + ][    ]$coin->name$coin->symbol$coin->difficulty$blocks$blocks$balance$btc$connections$coin->price$coin->reward$index
    "; +echo "

    "; + +////////////////////////////////////////////////////////////////////////////////////// + +echo ""; +//showTableSorter('maintable'); +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +//$transactions = $remote->listsinceblock(''); +$ts = $remote->listtransactions('', 10); + +$res_array = array(); +foreach($ts as $val) +{ + $t = $val['time']; + $res_array[$t] = $val; +} + +krsort($res_array); +foreach($res_array as $transaction) +{ + $block = null; + if(isset($transaction['blockhash'])) + $block = $remote->getblock($transaction['blockhash']); + + $d = datetoa2($transaction['time']); + + echo ""; + echo ""; + + if($block) + echo ""; + else + echo ""; + + echo ""; + echo ""; + + if(isset($transaction['confirmations'])) + echo ""; + else + echo ""; + + echo ""; + echo ""; +} + +echo "
    TimeHeightCategoryAmountConfirmations
    $d{$block['height']}{$transaction['category']}{$transaction['amount']}{$transaction['confirmations']}"; + if(isset($transaction['address'])) + { + $address = $transaction['address']; + echo "address $address
    "; + } + +// if($block) foreach($block['tx'] as $i=>$tx) +// echo "tx-$i $tx
    "; + + echo "
    "; + + + + + diff --git a/web/yaamp/modules/site/common.php b/web/yaamp/modules/site/common.php new file mode 100644 index 0000000..9de5052 --- /dev/null +++ b/web/yaamp/modules/site/common.php @@ -0,0 +1,231 @@ +Memcache  +//Eval  +//Jobs  +?> + +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  +Monitor  +Conns  +EmptyMarket  +New  +Rental  + +
    + +
    CREATE COIN +
    UPDATE PRICE + +









    +









    +









    +









    + + + + + diff --git a/web/yaamp/modules/site/common_results.php b/web/yaamp/modules/site/common_results.php new file mode 100644 index 0000000..b03a30e --- /dev/null +++ b/web/yaamp/modules/site/common_results.php @@ -0,0 +1,576 @@ +
    "; + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$total_coins = 0; +$total_workers = 0; +$total_hashrate = 0; +$total_hashrate_bad = 0; + +$algos = array(); +foreach(yaamp_get_algos() as $algo) +{ + $algo_norm = yaamp_get_algo_norm($algo); + + $price = controller()->memcache->get_database_scalar("current_price-$algo", + "select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + + $norm = $price*$algo_norm; + $norm = take_yaamp_fee($norm, $algo); + + $algos[] = array($norm, $algo); +} + +function cmp($a, $b) +{ + return $a[0] < $b[0]; +} + +usort($algos, 'cmp'); +foreach($algos as $item) +{ + $norm = $item[0]; + $algo = $item[1]; + + $algo_color = getAlgoColors($algo); + $algo_norm = yaamp_get_algo_norm($algo); + +// debuglog($algo); + $coins = getdbocount('db_coins', "enable and auto_ready and algo=:algo", array(':algo'=>$algo)); + $count = getdbocount('db_workers', "algo=:algo", array(':algo'=>$algo)); + + $total_coins += $coins; + $total_workers += $count; + + $hashrate = dboscalar("select hashrate from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $hashrate_bad = dboscalar("select hashrate_bad from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $bad = ($hashrate+$hashrate_bad)? round($hashrate_bad * 100 / ($hashrate+$hashrate_bad), 1): ''; + + $total_hashrate += $hashrate; + $total_hashrate_bad += $hashrate_bad; + + $hashrate = $hashrate? Itoa2($hashrate).'h/s': '-'; + $hashrate_bad = $hashrate_bad? Itoa2($hashrate_bad).'h/s': '-'; + + $hashrate_jobs = yaamp_rented_rate($algo); + $hashrate_jobs = $hashrate_jobs>0? Itoa2($hashrate_jobs).'h/s': ''; + + $price = dboscalar("select price from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $price = $price? mbitcoinvaluetoa($price): '-'; + + $rent = dboscalar("select rent from hashrate where algo=:algo order by time desc limit 1", array(':algo'=>$algo)); + $rent = $rent? mbitcoinvaluetoa($rent): '-'; + + $norm = mbitcoinvaluetoa($norm); + + $t = time() - 24*60*60; + $avgprice = dboscalar("select avg(price) from hashrate where algo=:algo and time>$t", array(':algo'=>$algo)); + $avgprice = $avgprice? mbitcoinvaluetoa(take_yaamp_fee($avgprice, $algo)): '-'; + + $t1 = time() - 24*60*60; + $total1 = dboscalar("select sum(amount*price) from blocks where category!='orphan' and time>$t1 and algo=:algo", array(':algo'=>$algo)); + $hashrate1 = dboscalar("select avg(hashrate) from hashrate where time>$t1 and algo=:algo", array(':algo'=>$algo)); + + if($algo == 'sha256') + $btcmhday1 = $hashrate1 != 0? mbitcoinvaluetoa($total1 / $hashrate1 * 1000000 * 1000000): ''; + else + $btcmhday1 = $hashrate1 != 0? mbitcoinvaluetoa($total1 / $hashrate1 * 1000000 * 1000): ''; + + $fees = yaamp_fee($algo); + + $stratum = getdbosql('db_stratums', "algo=:algo", array(':algo'=>$algo)); + $isup = Booltoa($stratum); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + if($bad > 10) + echo ""; + else if($bad > 5) + echo ""; + else + echo ""; + + if($norm>0) + echo ""; + else + echo ""; + + echo ""; + echo ""; + + if($btcmhday1 != '-' && $btcmhday1 > $avgprice*1.1) + echo ""; + else if($btcmhday1 != '-' && $btcmhday1*1.3 < $avgprice) + echo ""; + else if($btcmhday1 != '-' && $btcmhday1*1.2 < $avgprice) + echo ""; + else if($btcmhday1 != '-' && $btcmhday1*1.1 < $avgprice) + echo ""; + else + echo ""; + + echo ""; +} + +$bad = ($total_hashrate+$total_hashrate_bad)? round($total_hashrate_bad * 100 / ($total_hashrate+$total_hashrate_bad), 1): ''; +$total_hashrate = Itoa2($total_hashrate).'h/s'; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo "
    AlgoCMFRateRentBadNowRentNorm24E24A
    $algo$isup$coins$count{$fees}%$hashrate$hashrate_jobs{$bad}%{$bad}%{$bad}%$price$price$rent$avgprice$btcmhday1$btcmhday1$btcmhday1$btcmhday1$btcmhday1
    $total_coins$total_workers$total_hashrate{$bad}%

    "; + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +$markets = getdbolist('db_balances', "1 order by name"); +$total_balance = 0; +$total_onsell = 0; +$total_total = 0; + +echo ""; +echo ""; + +echo ""; +echo ""; + +foreach($markets as $market) + echo ""; + +echo ""; + +echo ""; +echo ""; + +echo ""; +foreach($markets as $market) +{ + $onsell = bitcoinvaluetoa(dboscalar("select sum(amount*bid) from orders where market='$market->name'")); + + if($onsell > 0.2) + echo ""; + else if($onsell > 0.1) + echo ""; + else + echo ""; + + $total_onsell += $onsell; +} + +$total_onsell = bitcoinvaluetoa($total_onsell); + +echo ""; +echo ""; + +echo ""; +foreach($markets as $market) +{ + $balance = bitcoinvaluetoa($market->balance); + + if($balance > 0.250) + echo ""; + else if($balance > 0.200) + echo ""; + else + echo ""; + + $total_balance += $balance; +} + +$total_balance = bitcoinvaluetoa($total_balance); + +echo ""; +echo ""; + +echo ""; +foreach($markets as $market) +{ + $total = bitcoinvaluetoa($market->balance + dboscalar("select sum(amount*bid) from orders where market='$market->name'")); + + echo ""; + $total_total += $total; +} + +$total_total = bitcoinvaluetoa($total_total); + +echo ""; +echo ""; +echo "
    $market->nameTotal
    shit$onsell$onsell$onsell$total_onsell
    btc$balance$balance$balance$total_balance
    total$total$total_total

    "; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +$minsent = time()-2*60*60; +$list = getdbolist('db_markets', "lastsent<$minsent and lastsent>lasttraded order by lastsent"); + +echo ""; +echo ""; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($list as $market) +{ + $price = bitcoinvaluetoa($market->price); + $coin = getdbo('db_coins', $market->coinid); + $lowsymbol = strtolower($coin->symbol); + + if($market->name == 'cryptsy') + $marketurl = "https://www.cryptsy.com/markets/view/$market->marketid"; + + else if($market->name == 'bittrex') + $marketurl = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($market->name == 'mintpal') + $marketurl = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($market->name == 'poloniex') + $marketurl = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($market->name == 'bleutrade') + $marketurl = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else if($market->name == 'c-cex') + $marketurl = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($market->name == 'yobit') + $marketurl = "https://yobit.net/en/trade/$coin->symbol/BTC"; + +// echo ""; + $algo_color = getAlgoColors($coin->algo); + echo ""; + + echo ""; + echo ""; + + echo ""; + + $sent = datetoa2($market->lastsent); + $traded = datetoa2($market->lasttraded); + + echo ""; + echo ""; + + echo ""; + echo ""; +} + +echo "
    NameExchangeSentTraded
    $coin->name ($coin->symbol)$market->name$sent ago$traded agoclear

    "; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +$orders = getdbolist('db_orders', "1 order by (amount*bid) desc"); + +echo ""; +//showTableSorter('maintable'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; + +$totalvalue = 0; +$totalbid = 0; + +foreach($orders as $order) +{ + $coin = getdbo('db_coins', $order->coinid); + if(!$coin) continue; + + $lowsymbol = strtolower($coin->symbol); + + if($order->market == 'cryptsy') + $marketurl = "https://www.cryptsy.com/markets/view/{$coin->symbol}_BTC"; + + else if($order->market == 'bittrex') + $marketurl = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($order->market == 'mintpal') + $marketurl = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($order->market == 'poloniex') + $marketurl = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($order->market == 'c-cex') + $marketurl = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($order->market == 'bleutrade') + $marketurl = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else + $marketurl = ""; + +// echo ""; + $algo_color = getAlgoColors($coin->algo); + echo ""; + + $created = datetoa2($order->created). ' ago'; + $price = $order->price? bitcoinvaluetoa($order->price): ''; + + $price = bitcoinvaluetoa($order->price); + $bid = bitcoinvaluetoa($order->bid); + $value = bitcoinvaluetoa($order->amount*$order->price); + $bidvalue = bitcoinvaluetoa($order->amount*$order->bid); + $totalvalue += $value; + $totalbid += $bidvalue; + $bidpercent = $value>0? round(($value-$bidvalue)/$value*100, 1): 0; + $amount = round($order->amount, 3); + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo $bidvalue>0.01? "": ""; + +// echo ""; + echo ""; +} + +$bidpercent = $totalvalue>0? round(($totalvalue-$totalbid)/$totalvalue*100, 1): ''; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo "
    NameExchangeCreatedQuantityAskBidValue
    $coin->name$order->market$created$amount$price$bid ({$bidpercent}%)$bidvalue$bidvalue"; +// echo "[cancel] "; +// echo "[sell]"; +// echo "
    Total$totalvalue$totalbid ({$bidpercent}%)

    "; + +/////////////////////////////////////////////////////////////////////////////////////// + +echo "
      "; + +////////////////////////////////////////////////////////////////////////////////// + +function cronstate2text($state) +{ + switch($state) + { + case 0: + return ''; + case 1: + return 'new coins'; + case 2: + return 'trading'; + case 3: + return 'markets'; + case 4: + return 'blocks'; + case 5: + return 'sell'; + case 6: + return 'find2'; + } +} + +//$state_block = memcache_get($this->memcache->memcache, 'cronjob_block_state'); +$state_main = memcache_get($this->memcache->memcache, 'cronjob_main_state'); +$btc = getdbosql('db_coins', "symbol='BTC'"); + +echo ""; +for($i=0; $i<10; $i++) +{ +// if($i != $state_block-1 && $state_block>0) +// { +// $state = memcache_get($this->memcache->memcache, "cronjob_block_state_$i"); +// if($state) echo "block $i "; +// } + + if($i != $state_main-1 && $state_main>0) + { + $state = memcache_get($this->memcache->memcache, "cronjob_main_state_$i"); + if($state) echo "main $i "; + } +} + +echo ""; + +$block_time = sectoa(time()-memcache_get($this->memcache->memcache, "cronjob_block_time_start")); +$loop2_time = sectoa(time()-memcache_get($this->memcache->memcache, "cronjob_loop2_time_start")); +$main_time2 = sectoa(time()-memcache_get($this->memcache->memcache, "cronjob_main_time_start")); + +$main_time = sectoa(memcache_get($this->memcache->memcache, "cronjob_main_time")); +$main_text = cronstate2text($state_main); + +echo "*** main ($main_time) $state_main $main_text ($main_time2), loop2 ($loop2_time), block ($block_time)
    "; + +$topay = dboscalar("select sum(balance) from accounts where coinid=$btc->id"); //here: take other currencies too +$topay2 = bitcoinvaluetoa(dboscalar("select sum(balance) from accounts where coinid=$btc->id and balance>0.001")); + +$renter = dboscalar("select sum(balance) from renters"); + +$stats = getdbosql('db_stats', "1 order by time desc"); +$margin2 = bitcoinvaluetoa($btc->balance - $topay - $renter + $stats->balances + $stats->onsell + $stats->wallets); + +$margin = bitcoinvaluetoa($btc->balance - $topay - $renter); + +$topay = bitcoinvaluetoa($topay); +$renter = bitcoinvaluetoa($renter); + +$immature = dboscalar("select sum(amount*price) from earnings where status=0"); +$mints = dboscalar("select sum(mint*price) from coins where enable"); +$off = $mints-$immature; + +$immature = bitcoinvaluetoa($immature); +$mints = bitcoinvaluetoa($mints); +$off = bitcoinvaluetoa($off); + +echo "Bitstamp $mining->usdbtc, "; +echo "wallet $btc->balance, next payout $topay2
    "; +echo "pay $topay, renter $renter, marg $margin, $margin2
    "; +echo "mint $mints immature $immature off $off
    "; + +echo '
    '; + +////////////////////////////////////////////////////////////////////////////////////////////////// + +echo "
    "; +//echo "
    "; +echo "
    "; + +/////////////////////////////////////////////////////////////////////////// + +$db_blocks = getdbolist('db_blocks', "1 order by time desc limit 50"); + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($db_blocks as $db_block) +{ + $d = datetoa2($db_block->time); + if(!$db_block->coin_id) + { + $reward = bitcoinvaluetoa($db_block->amount); + + $algo_color = getAlgoColors($db_block->algo); + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + continue; + } + + $coin = getdbo('db_coins', $db_block->coin_id); + if(!$coin) + { + debuglog("coin not found $db_block->coin_id"); + continue; + } + + $height = number_format($db_block->height, 0, '.', ' '); + $diff = Itoa2($db_block->difficulty, 3); + + $algo_color = getAlgoColors($coin->algo); + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; +} + + +echo "
    NameAmountDiffBlockTimeStatus
    Rental ($db_block->algo)$reward BTC$d ago"; + echo "Confirmed"; + echo "
    $coin->name$db_block->amount $coin->symbol$diff$height$d ago"; + + if($db_block->category == 'orphan') + echo "Orphan"; + + else if($db_block->category == 'immature') + echo "Immature ($db_block->confirmations)"; + + else if($db_block->category == 'generate') + echo "Confirmed"; + + else if($db_block->category == 'new') + echo "New"; + + echo "

    "; + +echo "
    "; + + diff --git a/web/yaamp/modules/site/connections.php b/web/yaamp/modules/site/connections.php new file mode 100644 index 0000000..7567f7a --- /dev/null +++ b/web/yaamp/modules/site/connections.php @@ -0,0 +1,57 @@ +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  +Big Miners  +EmptyMarket  + +
    + +









    +









    +









    +









    + + + +end; + + + + + + diff --git a/web/yaamp/modules/site/connections_results.php b/web/yaamp/modules/site/connections_results.php new file mode 100644 index 0000000..299e9d0 --- /dev/null +++ b/web/yaamp/modules/site/connections_results.php @@ -0,0 +1,48 @@ +"; + +//echo ""; +showTableSorter('maintable'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($list as $conn) +{ + echo ""; + + $d1 = sectoa($conn->idle); + $d2 = datetoa2($conn->created); + $d3 = datetoa2($conn->last); + $b = Booltoa($conn->last == $last); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; +} + +echo "
    IDUserHostDbIdleCreatedLast
    $conn->id$conn->user$conn->host$conn->db$d1$d2$d3$b

    "; + + + + diff --git a/web/yaamp/modules/site/diff.php b/web/yaamp/modules/site/diff.php new file mode 100644 index 0000000..8357082 --- /dev/null +++ b/web/yaamp/modules/site/diff.php @@ -0,0 +1,36 @@ +
    + +
    +
    YAAMP DIFFICULTY
    +
    + +

    By default, yammp will adjust the difficulty of your miner automatically over time until +you have from 5 to 15 submits per minute. It's a good trade off between bandwidth and accuracy.

    + +

    You can also set a fixed custom difficulty using the password parameter. For example, +if you want to set the difficulty to 64, you would use:

    + +
    +-o stratum+tcp://yaamp.com:3433 -u wallet_adress -p d=64
    +
    + +

    Here are the accepted values for the custom diff:

    + +

    Scrypt, Scrypt-N and Neoscrypt: from 2 to 65536

    + +

    X11, X13, X14 and X15: from 0.002 to 0.512

    + +

    Lyra2: from 0.01 to 2048

    + +

    If the difficulty is set higher than one of the a mined coins, it will be forced down to fit + the lowest coin's difficulty.

    + +









    +









    + + + + diff --git a/web/yaamp/modules/site/earning.php b/web/yaamp/modules/site/earning.php new file mode 100644 index 0000000..828f9c5 --- /dev/null +++ b/web/yaamp/modules/site/earning.php @@ -0,0 +1,50 @@ + +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  + +
    + +









    +









    +









    +









    + + + + + diff --git a/web/yaamp/modules/site/earning_results.php b/web/yaamp/modules/site/earning_results.php new file mode 100644 index 0000000..b455aa4 --- /dev/null +++ b/web/yaamp/modules/site/earning_results.php @@ -0,0 +1,74 @@ +"; +//showTableSorter('maintable'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +//echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($earnings as $earning) +{ +// if(!$earning) debuglog($earning); + $coin = getdbo('db_coins', $earning->coinid); + if(!$coin) continue; + + $user = getdbo('db_accounts', $earning->userid); + if(!$user) continue; + + $block = getdbo('db_blocks', $earning->blockid); + if(!$block) continue; + + $t1 = datetoa2($earning->create_time). ' ago'; + $t2 = datetoa2($earning->mature_time). ' ago'; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + echo ""; + +// echo ""; + echo ""; + +// if($block->category == 'generate' && $earning->status == 0) +// { +// $earning->status = 1; +// $earning->mature_time = time()-100*60; +// $earning->save(); +// } +} + +echo "
    NameWalletStatusAmountQuantityBlockStatusSent
    $coin->name ($coin->symbol_show)$user->username$earning->amount$block->height$block->category ($block->confirmations) $earning->status$t1 $t2 + [clear] + [delete] + $earning->tx
    "; + + + + + + + + + + + + diff --git a/web/yaamp/modules/site/emptymarkets.php b/web/yaamp/modules/site/emptymarkets.php new file mode 100644 index 0000000..d079651 --- /dev/null +++ b/web/yaamp/modules/site/emptymarkets.php @@ -0,0 +1,34 @@ +"; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +$list = dbolist("SELECT coins.id as coinid, markets.id as marketid FROM coins, markets WHERE coins.installed AND + coins.id=markets.coinid AND (markets.deposit_address IS NULL or (message is not null and message!='')) order by markets.id desc"); +foreach($list as $item) +{ + $coin = getdbo('db_coins', $item['coinid']); + $market = getdbo('db_markets', $item['marketid']); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; +} + +echo "
    CoinMarketPriceMessageDeposit
    $coin->name$market->name$market->price$market->message$market->deposit_address
    "; + +echo '









    '; +echo '









    '; + diff --git a/web/yaamp/modules/site/eval.php b/web/yaamp/modules/site/eval.php new file mode 100644 index 0000000..95e6f19 --- /dev/null +++ b/web/yaamp/modules/site/eval.php @@ -0,0 +1,22 @@ +
    +
    + +

    + + +
    + +









    +









    +









    + + + +end; + + + diff --git a/web/yaamp/modules/site/exchange.php b/web/yaamp/modules/site/exchange.php new file mode 100644 index 0000000..ed50f9b --- /dev/null +++ b/web/yaamp/modules/site/exchange.php @@ -0,0 +1,50 @@ + +Summary  +Coins  +Exchange  +Users  +Workers  +Version  +Earnings  +Payments  + +
    + +









    +









    +









    +









    + + + + + diff --git a/web/yaamp/modules/site/exchange_results.php b/web/yaamp/modules/site/exchange_results.php new file mode 100644 index 0000000..2e56b4e --- /dev/null +++ b/web/yaamp/modules/site/exchange_results.php @@ -0,0 +1,183 @@ +"; +//showTableSorter('maintable'); +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +//echo ""; +echo ""; +echo ""; + +$totalvalue = 0; +$totalbid = 0; + +foreach($orders as $order) +{ + $coin = getdbo('db_coins', $order->coinid); + $lowsymbol = strtolower($coin->symbol); + + if($order->market == 'cryptsy') + $marketurl = "https://www.cryptsy.com/markets/view/{$coin->symbol}_BTC"; + + else if($order->market == 'bittrex') + $marketurl = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($order->market == 'mintpal') + $marketurl = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($order->market == 'poloniex') + $marketurl = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($order->market == 'c-cex') + $marketurl = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($order->market == 'bleutrade') + $marketurl = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else + $marketurl = ""; + + echo ""; + + $created = datetoa2($order->created). ' ago'; + $price = $order->price? bitcoinvaluetoa($order->price): ''; + + $price = bitcoinvaluetoa($order->price); + $bid = bitcoinvaluetoa($order->bid); + $value = bitcoinvaluetoa($order->amount*$order->price); + $bidvalue = bitcoinvaluetoa($order->amount*$order->bid); + $totalvalue += $value; + $totalbid += $bidvalue; + $bidpercent = $value>0? round(($value-$bidvalue)/$value*100, 1): 0; + + echo ""; + echo ""; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo $bidvalue>0.01? "": ""; + +// echo ""; + echo ""; +} + +$bidpercent = $totalvalue? round(($totalvalue-$totalbid)/$totalvalue*100, 1): ''; + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +echo "
    NameExchangeCreatedQuantityAskBidValue
    $coin->name ($coin->symbol)$order->market$created$order->amount$price$bid ({$bidpercent}%)$bidvalue$bidvalue"; +// echo "[cancel] "; +// echo "[sell]"; +// echo "
    Total$totalvalue$totalbid ({$bidpercent}%)
    "; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +$exchanges = getdbolist('db_exchange', "1 order by send_time desc limit 150"); +//$exchanges = getdbolist('db_exchange', "status='waiting' order by send_time desc"); + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($exchanges as $exchange) +{ + $coin = getdbo('db_coins', $exchange->coinid); + $lowsymbol = strtolower($coin->symbol); + + if($exchange->market == 'cryptsy') + $marketurl = "https://www.cryptsy.com/markets/view/{$coin->symbol}_BTC"; + + else if($exchange->market == 'bittrex') + $marketurl = "https://bittrex.com/Market/Index?MarketName=BTC-$coin->symbol"; + + else if($exchange->market == 'mintpal') + $marketurl = "https://www.mintpal.com/market/$coin->symbol/BTC"; + + else if($exchange->market == 'poloniex') + $marketurl = "https://poloniex.com/exchange/btc_$coin->symbol"; + + else if($exchange->market == 'c-cex') + $marketurl = "https://c-cex.com/?p=$lowsymbol-btc"; + + else if($order->market == 'bleutrade') + $marketurl = "https://bleutrade.com/exchange/$coin->symbol/BTC"; + + else + $marketurl = ""; + + if($exchange->status == 'waiting') + echo ""; + else + echo ""; + + $sent = datetoa2($exchange->send_time). ' ago'; + $received = $exchange->receive_time? sectoa($exchange->receive_time-$exchange->send_time): ''; + $price = $exchange->price? bitcoinvaluetoa($exchange->price): bitcoinvaluetoa($coin->price); + $estimate = bitcoinvaluetoa($exchange->price_estimate); + $total = $exchange->price? bitcoinvaluetoa($exchange->quantity*$exchange->price): bitcoinvaluetoa($exchange->quantity*$coin->price); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo $total>0.01? "": ""; + + echo ""; + echo ""; +} + +echo "
    NameMarketCreatedQuantityEstimateSold PriceValue
    $coin->name ($coin->symbol)$exchange->market$sent$exchange->quantity$estimate$price$total$total"; + + if($exchange->status == 'waiting') + { + // echo "[clear]"; + echo "[del]"; + } + + echo "
    "; + + + + + + + + + + + + diff --git a/web/yaamp/modules/site/index.php b/web/yaamp/modules/site/index.php new file mode 100644 index 0000000..dd27542 --- /dev/null +++ b/web/yaamp/modules/site/index.php @@ -0,0 +1,154 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.cursor.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$height = '240px'; + +echo <<
    + + +
    + + + +
    +
    YET ANOTHER ANONYMOUS MINING POOL
    +
    + +
      + +
    • YAAMP is multipool multialgo with auto exchange to Bitcoin or any coin we mine.
    • +
    • We distribute hashpower in real time among the best coins.
    • +
    • No registration required.
    • +
    • Just plug in your stratum miner using your bitcoin wallet address as the username.
    • +
    • We also allow payouts in any currency we mine. Use your wallet address as the username.
    • +
    • Payouts are made automatically twice a day for all balances above 0.001 or 0.0001 on Sunday.
    • +
    • Blocks are distributed proportionally among valid submitted shares.
    • +
    • We also rent hashpower you can use on third party pools.
    • + +
      + +
    +

    + + + +
    +
    STRATUM SERVERS
    +
    + +
      + +
    • +

      + -o stratum+tcp://yaamp.com:PORT -u WALLET_ADDRESS -p xx

      +
    • + +
    • WALLET_ADDRESS can be of any currency we mine or a BTC address.
    • +
    • Use "-p c=SYMBOL" if yaamp does not recognize the currency correctly.
    • +
    • See the "Pool Status" area in the top right corner for PORT numbers.
    • + +
      + +
    +

    + + + +
    +
    LINKS
    +

    + + + + + + +
    + + + +
    +









    +
    + +
    +









    +
    + +
    + +









    +









    +









    +









    + + + +END; + diff --git a/web/yaamp/modules/site/memcached.php b/web/yaamp/modules/site/memcached.php new file mode 100644 index 0000000..48f7a58 --- /dev/null +++ b/web/yaamp/modules/site/memcached.php @@ -0,0 +1,56 @@ +refresh
    "; + +$memcache = controller()->memcache->memcache; +$a = memcache_get($this->memcache->memcache, 'url-map'); + +$res = array(); + +function cmp($a, $b) +{ + return $a[2] < $b[2]; +} + +foreach($a as $url=>$n) +{ + $d = memcache_get($this->memcache->memcache, "$url-time"); + $avg = $d/$n; + + $res[] = array($url, $n, $d, $avg); +} + +usort($res, 'cmp'); + +echo "
    "; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; + +foreach($res as $item) +{ +// debuglog("$i => $n"); + + $url = $item[0]; + $n = $item[1]; + $d = round($item[2], 3); + $avg = round($item[3], 3); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; +} + +echo "
    UrlCountTimeAverage
    $url$n$d$avg
    "; + + + + diff --git a/web/yaamp/modules/site/miners.php b/web/yaamp/modules/site/miners.php new file mode 100644 index 0000000..df2b2ca --- /dev/null +++ b/web/yaamp/modules/site/miners.php @@ -0,0 +1,88 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$height = '240px'; + +echo <<
    + + +
    + +
    +









    +









    +
    + +
    + +
    +









    +
    + +
    + +









    +









    +









    +









    + + + + +end; + + + + + diff --git a/web/yaamp/modules/site/mining.php b/web/yaamp/modules/site/mining.php new file mode 100644 index 0000000..847e80e --- /dev/null +++ b/web/yaamp/modules/site/mining.php @@ -0,0 +1,235 @@ +getState('yaamp-algo'); + +JavascriptFile("/extensions/jqplot/jquery.jqplot.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.dateAxisRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.barRenderer.js"); +JavascriptFile("/extensions/jqplot/plugins/jqplot.highlighter.js"); +JavascriptFile('/yaamp/ui/js/auto_refresh.js'); + +$height = '240px'; + +echo <<